Skip to Content

Programmation en Shell Bash sous Linux

But de ce document

Ce document me sert de mémo sur la programmation en Shell Bash. Il liste sans entrer dans le détail les principales possibilités en programmation Bash.

Premier script

Avec votre éditeur de texte préféré, saisir ces deux lignes dans le fichier « test.sh » :

#! /bin/bash
echo "Bonjour le monde"

Remarques :
- L’extension du fichier (.sh en l’occurrence) n’a pas d’importance. Il est même possible de ne pas en mettre.
- La première ligne est obligatoire car elle permet d’indiquer au shel qui va exécuter le script qu’il doit utiliser le Bash.

Pour faire fonctionner ce script, il faut commencer par le rendre exécutable :

$ chmod +x test.sh

Et nous pouvons le tester :

$ ./test.sh
Bonjour le monde

Les variables

Affecter une valeur à une variable

x=toto

ou en utilisant des guillemets pour protéger les espaces et les caractères spéciaux :

x="toto et tutu"

Remarque : Il ne faut pas mettre d’espace de chaque coté du signe égale

Cette commande permet d’initialiser la variable avec la valeur indiquée seulement si celle-ci est vide :

x=${x:-valeur par défaut}

Récupérer le résultat d’une commande

x=$(ls /etc)

ou :

x=`ls /etc`

Afficher le contenu d’une variable

echo $x

ou :

echo ${x}

Les tableaux

Affecter une valeur à un tableau :

$ tab[0]=toto

Lire le contenu du tableau

$ echo ${tab[0]}

Remarque : Dans ce cas, il est obligatoire d’utiliser les accolades pour lire le contenu

Protection des expressions

Le «  backslash  » permet de protéger tous les caractères spéciaux y compris lui même :

echo \* \# \$ \' \" \\
* # $ ' " \

Le « guillemet double » permet de protéger tous les caractères sauf lui même qu’il faut protéger avec un «  backslash  » et le « $ » ce qui permet d’interpréter le contenu des variables :

$ echo "* # $ \ ' \" x=$x"
* # $ \ ' " x=toto

L’apostrophe permet de protéger tous les caractères sauf elle même :

$ echo '* # $ \ " x=$x'
* # $ \ " x=$x

Les calculs

Addition :

$ a=10; b=20; c=$((a+b)); echo "a+b=$c"
a+b=30

Multiplication et division :

$ a=10; b=20; c=2; d=$((a*b/c)); echo "a*b/c=$d"
a*b/c=100

Le traitement des chaînes de caractères

Concaténation de chaînes de caractères :

$ a=toto; b=tutu; c="$a et $b"; echo $c
toto et tutu

Supprime les 5 premiers caractères de la chaîne :

$ fich="test.tar.gz" ; echo ${fich:5}
tar.gz

Extrait 3 caractères à partir du cinquième :

tony@etch:~$ fich="test.tar.gz" ; echo ${fich:5:3}
tar

Le signe « % » permet de supprimer le plus petit suffixe correspond à l’expression régulière :

$ fich="test.tar.gz" ; echo ${fich%.*}
test.tar

Le signe « %% » permet de supprimer le plus grand suffixe correspond à l’expression régulière :

$ fich="test.tar.gz" ; echo ${fich%%.*}
test

Le signe « # » permet de supprimer le plus petit préfixe correspond à l’expression régulière :

$ fich="test.tar.gz" ; echo ${fich#*.}
tar.gz

Le signe « ## » permet de supprimer le plus grand préfixe correspond à l’expression régulière :

$ fich="test.tar.gz" ; echo ${fich##*.}
gz

Remplace la première sous-chaîne trouvée par celle proposée :

fich="test.tar.gz" ; echo ${fich/.tar.gz/.tgz}
test.tgz

Remplace toutes les sous-chaînes trouvées par celle proposée :

fich="test.tar.gz.tar.gz" ; echo ${fich//.tar.gz/.tgz}
test.tgz.tgz

La commande suivante permet de retrouver la longueur d’une chaîne :

$ fich="test.tar.gz" ; echo ${#fich}
11

Utilisation des pipelines

Le caractère « | » permet d’envoyer la sortie d’une commande sur l’entrée de la commande suivante. Exemple :

$ df -h | tail -1 | sed "s/  */ /g" | cut -d " " -f 4
4,2G

La même chose avec «  awk  » :

$ df -h | tail -1 | awk '{print $4}'
4,2G

L’enchaînement de commandes sur une même ligne

Ces commandes sont exécutées les unes derrières les autres :

$ echo debut; echo fin
debut
fin

La dernière commande est exécuté uniquement si la commande précédente c’est exécutée avec succès :

$ echo debut; true && echo fin
debut
fin

La dernière commande est exécuté uniquement si la commande précédente a retourné une erreur :

$ echo debut; false || echo fin
debut
fin

Les redirections

Envoie la sortie standard dans le fichier ls.txt :

$ ls /etc/ >ls.txt

Envoie la sortie standard à la fin du fichier ls.txt :

$ ls /etc/ >>ls.txt

Envoie la sortie d’erreur dans le fichier ls.err :

$ ls /toto 2>ls.err

Redirection de la sortie standard et de la sortie d’erreur dans deux fichiers séparés :

$ ls /etc/ >ls.txt 2>ls.err

Envoie la sortie d’erreur et la sortie standard dans le fichier ls.txt :

$ ls /toto 2>&1 >ls.txt

La même chose en plus court :

$ ls /toto &>ls.txt

Redirige le résultat de deux commandes dans ls.txt

(ls /etc; ls /toto) &>ls.txt

Opérateur de test [ ]

L’opérateur de test « [ ] » retourne « vrai » si la condition est vrai et « faux » dans le cas contraire. Donc cette ligne exécutera la commande «  echo  » seulement si le fichier existe :

$ [ -f ./test.sh ] && echo "OK"

Arguments les plus courant de l’opérateur

Option Vrai si
-e fichier Le fichier indiqué existe
-d répertoire Le répertoire indiqué existe
-f fichier Le fichier indiqué est un fichier régulier
-h fichier Le fichier indiqué est un lien symbolique
chaine1 = chaine2 Les deux chaînes sont identiques
val1 -eq val2 Les deux valeurs sont égales
val1 -ge val2 val1 >= val2
val1 -gt val2 val1 > val2
val1 -le val2 val1 <= val2
val1 -lt val2 val1 < val2
val1 -ne val2 val1 != val2

Condition (if...else...fi)

Syntaxe générale :

if condition ; then
 ...
else
 ...
fi

Cette instruction s’utilise en générale avec l’opérateur de test détaillé au chapitre précédent. Exemple :

if [ -f ./test.sh ] ; then
 echo "OK"
fi

Ou sur une seule ligne :

if [ -f ./test.sh ] ; then echo "OK" ; fi

Condition « case...esac »

Syntaxe générale :

case expression in
 motif1) commande 1 ;;
 motif2) commande 2 ;;
 ...
esac

Le motif peut contenir
- * : N’importe quelle chaîne de caractères
-  ? : N’importe quel caractère
- [abc] : Alternance de caractères
- [A-Z] : Suite de caractères
- | : OU permettant de combiner plusieurs motifs

Exemple :

#! /bin/bash

while [ -n "$1" ]; do
 ping -c 1 -w 1 $1 >/dev/null 2>&1
 case $? in
   0) echo -e "$1 \t OK !";;
   1) echo -e "$1 \t injoignable !";;
   2) echo -e "$1 \t inexistant !";;
 esac
 shift
done

Boucle « while...done »

Syntaxe générale :

while condition ; do
 ...
done

Exemple :

i=0 ;
while [ $i -lt 5 ] ; do
 i=$((i+1))
 echo $i
done

La même chose sur une ligne :

i=0 ; while [ $i -lt 5 ] ; do i=$((i+1)) ; echo $i ; done

Un autre exemple en utilisant l’instruction «  break  »permettant de sortie de la boucle :

i=0
while true ; do
       echo "$i avec break"
       i=$((i+1))
       [ $i -eq 5 ] && break
done

Boucle « until...done »

Syntaxe générale :

until condition ; do
 ...
done

Boucle « for...done »

Le fonctionnement de cette boucle est très différent de celui des autres langages.

Syntaxe :

for variale in liste_de_mots ; do
 ...
done

Exemple :

$ for var in a b c d ; do echo $var ; done

Renommer une série de fichiers :

for i in *.tgz ; do mv $i ${i%tgz}tar.gz ; done

Boucle « select...done » permet de réaliser des listes de choix

Exemple :

$ select var in a b c ; do echo $var ; done
1) a
2) b
3) c
#?      

Fonctions

Voici la syntaxe générale d’une fonction :

function nom_de_la_fonction ()
{
...
}

Passage d’arguments

$0 : Contient le nom du script

$1 : Premier argument ($2 : Deuxième argument,..)

$10 : Dixième argument (Attention : $10 retourne le premier argument suivi de 0

$* : Tous les arguments mais sans protéger les espaces et caractères spéciaux

"$@" : Tous les arguments mais en protégeant les espaces et caractères spéciaux

$# : Nombre d’arguments de la fonction

La commande «  shift  » permet de décaler les variables (Le contenu de $1 est remplacé par celui de $2,..). Cela permet par exemple de lire le contenu de toute les variables :

while [ -n "$1" ] ; do
       echo $1
       shift
done

Affiche la lites des arguments passés au script :

for i in "$@" ; do
 echo $i
done

La même chose en plus court :

for i ; do
 echo $i
done

Traitement des options dans un script

Traitement des options sans arguments

L’instruction «  getopts  » permet de traiter les options passées en ligne de commande. Dans cet exemple, nous avons comme options « -a, -b et/ou -c ». L’ordre des options n’a pas d’importance et il est possible de les coller ou pas.

while getopts "abc" option ; do
 case $option in
   a) echo "Option a";;
   b) echo "Option b";;
   c) echo "Option c";;
 esac
done

Test :

$ ./traite_arguments.sh -c -ab
Option c
Option a
Option b

Traitement des options avec argument

Si une option nécessite un argument, il faut la faire suivre du signe «  : » et la valeur de l’argument sera accessible dans la variable « OPTARG ». Exemple :

while getopts "abc:d:" option ; do
 case $option in
   a) echo "Option a";;
   b) echo "Option b";;
   c) echo "Option c, argument $OPTARG";;
   d) echo "Option d, argument $OPTARG";;
 esac
done

Test :

$ ./traite_arguments2.sh -c toto -ab -d tutu
Option c, argument toto
Option a
Option b
Option d, argument tutu

Traitement des erreurs

Pour gérer les erreurs, il faut ajouter «  : » dans la liste des options possibles. Cela permettra de traiter les options sans arguments. Et pour traiter les options inconnues «  getopts  » retourne «  ? » qu’il suffit de traiter dans le script :

while getopts ":abc:d:" option ; do
 case $option in
   a) echo "Option a";;
   b) echo "Option b";;
   c) echo "Option c, argument $OPTARG";;
   d) echo "Option d, argument $OPTARG";;
   :) echo "Argument manquant pour l'option -$OPTARG";;
   ?) echo "Option -$OPTARG inconnue";;
 esac
done

Traitement des options longues sans arguments

Par défaut «  getopts  » ne sais pas traiter les options longues mais une petite astuce permet de contourner ce problème pour les options sans argument. Pour cela il suffit de d’ajouter dans les options « - » et de traiter en premier le cas des options longues.

while getopts ":hv-:" option ; do
 if [ "$option" = "-" ] ; then
   case $OPTARG in
     help)    option=h;;
     version) option=v;;
     *)       echo "Option $OPTARG inconnue";;
   esac
 fi
 case $option in
   h) echo "Option h";;
   v) echo "Option v";;
   ?) echo "Option -$OPTARG inconnue";;
 esac
done

Test :

$ ./traite_arguments3.sh --version -h -a
Option v
Option h
Option -a inconnue

Pour avoir plus d’informations

Pour avoir plus d’explications sur la programmation en Bash mais également avec Sed, Awk, Perl, Tcl, Tk, Python, Ruby ..., je vous conseille la lecture de cet excellent livre dont deux chapitres sont disponibles au format PDF :
- http://www.editions-eyrolles.com/Livre/9782212110289/langages-de-scripts-sous-linux

Historique des modifications

Version Date Commentaire
0.1 14/08/08 Création par Tony GALMICHE