Bash
| 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
- http://abs.traduc.org/abs-fr/ — Advanced Bash-Scripting Guide, traduction française
- http://tldp.org/LDP/abs/html/ — version originale anglaise
- http://man.developpez.com/man1/bash.1.php — page de manuel bash traduite
- http://mywiki.wooledge.org/BashPitfalls — pièges classiques du scripting Bash