Bash

De wiki.nexiat.fr
Aller à la navigation Aller à la recherche
Fiche express
Domaine Interpréteur de commandes / scripting shell
Shells apparentés ksh, dash (/bin/sh sur Debian/Ubuntu), zsh
Exécuter un script bash script.sh · ./script.sh (exécutable + shebang)
Charger dans le shell courant . script.sh ou source script.sh
Voir aussi Debug shell · Trap · Grep · Sed · Awk

Bash (Bourne Again SHell) est l'interpréteur de commandes par défaut de la plupart des distributions Linux, et le langage de script le plus répandu pour l'administration système. Il descend du Bourne shell historique et reste très proche de ksh sur la syntaxe courante — un script écrit pour l'un se porte souvent sans modification vers l'autre, en dehors de quelques extensions propres à chacun (tableaux associatifs, [[ ]] étendu...). Sur les distributions Debian/Ubuntu, /bin/sh pointe vers dash, un shell POSIX minimal plus rapide mais qui n'accepte pas les extensions Bash (tableaux, [[ ]], ${var//x/y}...) — un script utilisant ces extensions doit être invoqué explicitement avec bash, ou déclarer #!/bin/bash en shebang plutôt que #!/bin/sh.

Méthodes d'exécution d'un script

Quatre façons d'exécuter un script, avec des implications différentes sur le processus et les permissions requises :

Syntaxe Nouveau processus ? Permissions requises Effet
bash script.sh (ou sh script.sh) oui lecture seule lance un sous-shell qui interprète le fichier
./script.sh oui lecture + exécution identique à bash script.sh, mais via le shebang (#!) qui indique quel interpréteur utiliser
. script.sh (ou source script.sh) non lecture seule exécute le script dans le shell courant : les variables, alias et changements de répertoire (cd) faits par le script restent actifs après la fin du script
exec script.sh remplace le shell courant lecture + exécution le processus shell actuel est remplacé par le script (même PID) ; il n'y a rien « après » — le shell appelant disparaît

source (ou son alias .) est la méthode utilisée pour charger un fichier de variables d'environnement ou de fonctions réutilisables dans le shell courant — voir par exemple Lancement procedure stocke oracle.

Redirections

commande > /dev/null 2>&1   # stdout ET stderr vers /dev/null (l'ordre compte : 2>&1 après la redirection de stdout)
commande 2> /dev/null       # seul stderr est ignoré, stdout reste affiché
commande >> fichier         # ajoute à la fin du fichier au lieu de l'écraser
commande < fichier          # fichier comme entrée standard

Structures de contrôle

if commande; then
    ...
elif autre_commande; then
    ...
else
    ...
fi

while condition; do
    ...
done

until condition; do   # inverse de while : boucle tant que la condition est fausse
    ...
done

for var in liste; do
    ...
done

for (( c=1; c<=5; c++ )); do   # forme arithmétique de for, syntaxe C
    ...
done

case "$valeur" in
    modele1) commandes ;;
    modele2) commandes ;;
    *)       action_par_defaut ;;
esac

select variable in liste; do
    ...
done

Quelques idiomes utiles avec for/lecture ligne à ligne :

for i in "titi" "toto"; do echo "$i"; done
for i in {1..5}; do echo "$i"; done          # range 1 à 5
for i in {0..10..2}; do echo "$i"; done      # range avec pas de 2

# lire un fichier ligne à ligne (préférer cette forme à `for i in $(cat fichier)`,
# qui casse sur les espaces et les lignes vides)
while IFS= read -r ligne; do
    echo "$ligne"
done < fichier.txt

# idem depuis un pipe
cat fichier.txt | while IFS= read -r ligne; do
    echo "$ligne"
done

case supporte les motifs glob (pas les regex complètes) dans chaque branche — *, ?, [...] comme pour les wildcards de noms de fichiers, décrits plus bas.

Tests et opérateurs logiques

# opérateurs numériques
-eq  -ne  -lt  -gt  -le  -ge

# opérateurs de chaînes
=    !=   ==      # == fonctionne uniquement dans [[ ]], pas dans [ ] (POSIX)

# opérateurs logiques
-a   # AND (dans un seul [ ])
-o   # OR  (dans un seul [ ])
!    # NON
( )  # groupement

[ -d /etc -o "$valeur" -eq 12 ]
[ ! \( -d /etc -o "$valeur" -eq 12 \) ]

Tests sur les fichiers :

-f   fichier régulier          -r   lecture autorisée
-d   répertoire                -w   écriture autorisée
-c   fichier mode caractère    -x   exécution autorisée
-b   fichier mode bloc         -s   fichier non vide
-p   tube nommé (FIFO)         -e   le fichier existe
-L   lien symbolique           -u   bit SUID positionné
                                -g   bit SGID positionné

Wildcards (globbing)

*        # longueur variable, y compris zéro caractère
?        # un caractère unique
[...]    # une série ou une plage de caractères, ex: [a-c]
[!...]   # négation de la recherche (POSIX)

ls *[12]        # fichiers finissant par 1 ou 2
ls [a-c]?*[12]  # commence par a-c, au moins 2 caractères, finit par 1 ou 2
ls *[!3]        # ne finit pas par 3

Expansions de paramètres

Formes de substitution conditionnelle — elles ne s'appliquent que si la variable est vide ou non définie :

Syntaxe Effet
${x:-texte} si x est vide/non définie, retourne texte (sans modifier x)
${x:=texte} pareil, mais affecte aussi texte à x
${x:+texte} si x n'est pas vide, retourne texte (sinon chaîne vide)
${x:?texte} si x est vide, affiche texte sur stderr et arrête le script (équivalent d'un die)
TMP=""
VAR=${TMP:-9999}     # VAR=9999, TMP inchangée

: ${x:?"erreur : x doit être défini"}   # le ":" en tête ignore le code retour, seul l'effet du test compte

Découpage/troncature de chaîne (extraction de sous-chaîne, suppression de préfixe) :

VAR=azertyuiop
echo ${VAR:3}      # rtyuiop        (à partir de l'index 3)
echo ${VAR:3:4}     # rtyu           (index 3, longueur 4)

VAR=un_nom_variable.20060225.txt
echo ${VAR:(-12):8}   # 20060225      (12 caractères avant la fin, 8 pris)

VAR=abcABC123ABCabc
echo ${VAR#a*C}       # 123ABCabc     (supprime le plus court préfixe correspondant à "a*C")

Tableaux et longueur

VAR=azerty
echo ${#VAR}        # 6           (longueur de la chaîne)

VAR=( un deux trois quatre )
echo ${#VAR[@]}      # 4           (nombre d'éléments du tableau)
echo ${#VAR[0]}      # 2           (longueur du premier élément, "un")

Variables spéciales

$?         # code retour de la dernière commande
$$         # PID du shell courant
$!         # PID du dernier processus lancé en arrière-plan
$-         # options actives du shell
$0         # nom du script
$1 .. $9   # arguments positionnels
$#         # nombre total d'arguments
$*         # tous les arguments, en un seul mot "$1 $2 $3"
$@         # tous les arguments, en mots distincts "$1" "$2" "$3"
$PPID      # PID du processus parent
$LINENO    # numéro de la ligne en cours dans le script

$BASHPID (Bash 4+) diffère de $$ à l'intérieur d'un sous-shell ou d'une substitution de commande, où $$ continue de renvoyer le PID du shell parent :

echo -n "$BASHPID "; cut -d' ' -f4 /proc/self/stat
( echo -n "$BASHPID "; cut -d' ' -f4 /proc/self/stat )   # PID différent dans le sous-shell

shift décale les paramètres positionnels (par défaut de 1 cran) : $2 devient $1, etc. — utile pour consommer des arguments un par un dans une boucle.

readonly var rend une variable non modifiable pour le reste de la session (pas de marche arrière possible sans quitter le shell/sous-shell).

typeset -i force le typage numérique d'une variable, ce qui permet une évaluation arithmétique directe à l'affectation :

typeset -i var
var=7*6   # var vaut 42, pas la chaîne "7*6"

Caractères spéciaux dans les chaînes (echo -e)

\n     saut de ligne
\t     tabulation
\c     supprime le retour chariot final
\b     retour arrière (backspace)
\\     affiche un antislash littéral
\nnn   caractère à partir de son code octal

-- marque la fin des options sur la ligne de commande, utile quand un argument commence par - (ex. un nom de fichier) :

rm -- -i   # supprime le fichier nommé littéralement "-i", pas l'option -i de rm

Heredoc (insertion de texte)

cat >> /etc/security/limits.conf <<EOF
oracle soft nproc 2047
EOF

Un heredoc (<<EOF ... EOF) permet d'injecter un bloc de texte multi-lignes dans une commande sans jongler avec des échappements — très utilisé pour générer un fichier de configuration ou afficher une bannière (voir Issue).

Signaux et interruptions

Les signaux permettent d'intercepter une interruption (Ctrl-C, arrêt système...) ou d'envoyer un ordre simple à un autre processus. Voir Trap pour intercepter un signal dans un script (nettoyage garanti à la sortie), et Signal pour la liste des signaux standards.

Débogage

Voir Debug shell pour set -x/set -e et les techniques de journalisation d'un script en cours d'exécution.

Références

Voir aussi

  • Debug shellset -x/set -e, journalisation d'un script
  • Trap / Signal — interception de signaux, nettoyage à la sortie
  • Issue — gabarits d'en-tête de script, parsing d'options avec getopt
  • Grep · Sed · Awk — les compagnons incontournables d'un script shell