Crash course bash

Introduction

Le but est de donner quelques pistes pour la programmation de courts scripts en bash

Quelques notions

de base
  • processus (un shell s'exécute dans un processus)
  • variables d'environnement (les variables d'environnement sont propre à chaque processus, héritées par copie au lancement d'un processus fils, sauf celles qui n'ont pas été exportées)

avancées
  • expansion des arguments (le shell étend tous les arguments d'un programme avant de le lancer)
    • exemple: find . -name *.c
      • risque de ne pas marcher comme on veut s'il existe un fichier truc.c dans le répertoire courant.
      • protéger les métacaractères (entre apostrophes ou avec un backlash)
  • un espace est un séparateur

Mon premier script bash

bash peut être utilisé en ligne de commande, même pour des opérations complexes. Cependant, pour réutiliser plus facilement des fonctions ou les mettre à disposition d'autres utilisateurs, il peut être intéressant de créer des scripts.

script
  • fichier texte contenant des commandes simples ou complexes bash
  • à éditer avec tout éditeur UNIX
    • ALERT! attention aux fins de lignes incorrectes (CTRL-M) si le fichier provient d'un environnement Microsoft.
  • doit être exécutable si l'on ne veut pas le sourcer
    • chmod a+rx nom-du-script
    • ALERT! la première ligne du fichier texte devrait être #! /bin/bash (en particulier sur Ubuntu qui a remplacé /bin/sh par une version très limitée du standard POSIX ksh)

Exemple de script (créé via un "here document")
   cat > nom-du-script <<EOF
#! /bin/bash

date
EOF
   chmod a+rx nom-du-script

Lancement du script
  • soit le script est dans le path (voir echo $PATH): nom-du-script
  • soit le script n'est pas dans le path (ALERT! UNIX ne met en général pas le répertoire courant, '.', dans le PATH pour des raisons de sécurité -- par défaut), donner le chemin au script, par exemple:
    • /home/schaefer/bin/nom-du-script (absolu)
    • ./nom-du-script (relatif)
    • ../bin/nom-du-script (relatif)

Alternative: sourçage
  • Motivations
    • le lancement du script ci-dessus crée un nouveau processus bash et exécute le script dans ce nouveau contexte. Lorsque le script se termine, le contexte est détruit avec le processus. Exemple: si le script change le répertoire courant (avec cd), la modification NE SERA PAS vue dans l'appelant (processus parent)
    • dans certains cas, c'est trop lourd de créer un nouveau processus
    • dans certains cas, on veut véritablement modifier le contexte actuel (p.ex. changer des variables comme PATH), la méthode de lancement ne le permet pas!
  • Lecture/sourçage/inclusion
    • on écrit source nom-du-script (syntaxe alternative: . nom-du-script (point espace))
      • le shell courant exécute le script
      • on peut donc modifier le répertoire courant, les variables d'environnement, etc.

Un script plus complet

But de ce script: lister la taille des packages installés (Debian ou Ubuntu), par taille.

#! /bin/bash
# Lister les packages installés, par taille.

# Le fichier temporaire se trouvera dans /tmp (peu sûr) ou dans le répertoire pointé par la variable TMPDIR
# (voir info bash, puis Shell Parameter Expansion pour le fonctionnement du ${VAR-default}
# $0 réfère au nom du script.
# $(cmd) exécute la commande cmd. Son résultat (sortie standard texte) est remplacé dans le texte de commande (équivalent: `...`)
# Par convention, on met les variables non exportées en minuscules.
tmp_file=${TMPDIR-/tmp}/$(basename $0)_tmp

# Obtenir les statistiques de tous les packages installables et stocker dans
# le fichier $tmp_file, trié alphanumériquement par la 1ère colonne (nom du package)
apt-cache dumpavail \
   | awk 'BEGIN { what = ""; }
          /^Package: / { if (what != "") {
                            printf "%s %s\n", what, size;
                         }
                         what = $2;
                       }
          /^Installed-Size: / { size = $2; }
          END { if (what != "") {
                   printf "%s %s\n", what, size;
                }
              }' \
   | sort -k1,1 > $tmp_file

# Obtenir les packages installés (leurs noms), puis faire une jointure avec la taille.
# Trier numériquement sur la taille (colonne 2), avec le plus gros en premier.
dpkg --get-selections \
   | egrep '[[:space:]](install|hold)$' \
   | awk '{print $1'} \
   | sort \
   | join -1 1 -2 1 $tmp_file - \
   | sort -r -n -k2,2

NOTES
  • suppose LANG=C
  • on pourrait optimiser les entrées/sorties du script en inversant qui crée le fichier temporaire, ou en utilisant des tubes nommés (pipe). Encore que sort va de toute façon utiliser le disque.

Références

-- MarcSCHAEFER - 17 Jun 2010
Topic revision: r4 - 07 Nov 2011, MarcSCHAEFER
This site is powered by FoswikiCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding Foswiki? Send feedback