<h2>But de ce document</h2>
<p class="spip">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.</p>

<h2>Premier script</h2>
<p class="spip">Avec votre éditeur de texte préféré, saisir ces deux lignes dans le fichier « <strong class="spip">test.sh</strong> » :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>#! /bin/bash<br />
echo "Bonjour le monde"</code></div>
<p class="spip"><strong class="spip">Remarques</strong> : 
<br /></p>
<li> L’extension du fichier (.sh en l’occurrence) n’a pas d’importance. Il est même possible de ne pas en mettre.
<br /><li> 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.
<p class="spip">Pour faire fonctionner ce script, il faut commencer par le rendre exécutable :</p>

<p class="spip"><code class='spip_code' dir='ltr'>$ chmod +x test.sh</code></p>

<p class="spip">Et nous pouvons le tester :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>$ ./test.sh<br />
Bonjour le monde</code></div>
<h2>Les variables</h2>
<h3>Affecter une valeur à une variable</h3>
<p class="spip"><code class='spip_code' dir='ltr'>x=toto</code></p>

<p class="spip">ou en utilisant des guillemets pour protéger les espaces et les caractères spéciaux :</p>

<p class="spip"><code class='spip_code' dir='ltr'>x="toto et tutu"</code></p>

<p class="spip"><strong class="spip">Remarque</strong> : Il ne faut pas mettre d’espace de chaque coté du signe égale</p>

<p class="spip">Cette commande permet d’initialiser la variable avec la valeur indiquée seulement si celle-ci est vide :</p>

<p class="spip"><code class='spip_code' dir='ltr'>x=${x:-valeur par défaut}</code></p>

<h3>Récupérer le résultat d’une commande</h3>
<p class="spip"><code class='spip_code' dir='ltr'>x=$(ls /etc)</code></p>

<p class="spip">ou :</p>

<p class="spip"><code class='spip_code' dir='ltr'>x=`ls /etc`</code></p>

<h3>Afficher le contenu d’une variable</h3>
<p class="spip"><code class='spip_code' dir='ltr'>echo $x</code></p>

<p class="spip">ou :</p>

<p class="spip"><code class='spip_code' dir='ltr'>echo ${x}</code></p>

<h2>Les tableaux</h2>
<p class="spip">Affecter une valeur à un tableau :</p>

<p class="spip"><code class='spip_code' dir='ltr'>$ tab[0]=toto</code></p>

<p class="spip">Lire le contenu du tableau</p>

<p class="spip"><code class='spip_code' dir='ltr'>$ echo ${tab[0]}</code></p>

<p class="spip"><strong class="spip">Remarque</strong> : Dans ce cas, il est obligatoire d’utiliser les accolades pour lire le contenu</p>

<h2>Protection des expressions</h2>
<p class="spip">Le « <strong class="spip"> backslash </strong> » permet de protéger tous les caractères spéciaux y compris lui même :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>echo \* \# \$ \' \" \\<br />
* # $ ' " \</code></div>
<p class="spip">Le « <strong class="spip">guillemet double</strong> » permet de protéger tous les caractères sauf lui même qu’il faut protéger avec un « <strong class="spip"> backslash </strong> » et le « $ » ce qui permet d’interpréter le contenu des variables :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>$ echo "* # $ \ ' \" x=$x"<br />
* # $ \ ' " x=toto</code></div>
<p class="spip">L’apostrophe permet de protéger tous les caractères sauf elle même :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>$ echo '* # $ \ " x=$x'<br />
* # $ \ " x=$x</code></div>
<h2>Les calculs</h2>
<p class="spip">Addition :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>$ a=10; b=20; c=$((a+b)); echo "a+b=$c"<br />
a+b=30</code></div>
<p class="spip">Multiplication et division :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>$ a=10; b=20; c=2; d=$((a*b/c)); echo "a*b/c=$d"<br />
a*b/c=100</code></div>
<h2>Le traitement des chaînes de caractères</h2>
<p class="spip">Concaténation de chaînes de caractères :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>$ a=toto; b=tutu; c="$a et $b"; echo $c<br />
toto et tutu</code></div>
<p class="spip">Supprime les 5 premiers caractères de la chaîne :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>$ fich="test.tar.gz" ; echo ${fich:5}<br />
tar.gz</code></div>
<p class="spip">Extrait 3 caractères à partir du cinquième :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>tony@etch:~$ fich="test.tar.gz" ; echo ${fich:5:3}<br />
tar</code></div>
<p class="spip">Le signe « % » permet de supprimer le plus <strong class="spip">petit suffixe</strong> correspond à l’expression régulière :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>$ fich="test.tar.gz" ; echo ${fich%.*}<br />
test.tar</code></div>
<p class="spip">Le signe « %% » permet de supprimer le plus <strong class="spip">grand suffixe</strong> correspond à l’expression régulière :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>$ fich="test.tar.gz" ; echo ${fich%%.*}<br />
test</code></div>
<p class="spip">Le signe « # » permet de supprimer le plus <strong class="spip">petit préfixe</strong> correspond à l’expression régulière :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>$ fich="test.tar.gz" ; echo ${fich#*.}<br />
tar.gz</code></div>
<p class="spip">Le signe « ## » permet de supprimer le plus <strong class="spip">grand préfixe</strong> correspond à l’expression régulière :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>$ fich="test.tar.gz" ; echo ${fich##*.}<br />
gz</code></div>
<p class="spip">Remplace la première sous-chaîne trouvée par celle proposée :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>fich="test.tar.gz" ; echo ${fich/.tar.gz/.tgz}<br />
test.tgz</code></div>
<p class="spip">Remplace toutes les sous-chaînes trouvées par celle proposée :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>fich="test.tar.gz.tar.gz" ; echo ${fich//.tar.gz/.tgz}<br />
test.tgz.tgz</code></div>
<p class="spip">La commande suivante permet de retrouver la longueur d’une chaîne :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>$ fich="test.tar.gz" ; echo ${#fich}<br />
11</code></div>
<h2>Utilisation des pipelines</h2>
<p class="spip">Le caractère « | » permet d’envoyer la sortie d’une commande sur l’entrée de la commande suivante. Exemple :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>$ df -h | tail -1 | sed "s/  */ /g" | cut -d " " -f 4<br />
4,2G</code></div>
<p class="spip">La même chose avec « <strong class="spip"> awk </strong> » :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>$ df -h | tail -1 | awk '{print $4}'<br />
4,2G</code></div>
<h2>L’enchaînement de commandes sur une même ligne</h2>
<p class="spip">Ces commandes sont exécutées les unes derrières les autres :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>$ echo debut; echo fin<br />
debut<br />
fin</code></div>
<p class="spip">La dernière commande est exécuté uniquement si la commande précédente c’est exécutée avec succès :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>$ echo debut; true && echo fin<br />
debut<br />
fin</code></div>
<p class="spip">La dernière commande est exécuté uniquement si la commande précédente a retourné une erreur :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>$ echo debut; false || echo fin<br />
debut<br />
fin</code></div>
<h2>Les redirections</h2>
<p class="spip">Envoie la sortie standard dans le fichier ls.txt :</p>

<p class="spip"><code class='spip_code' dir='ltr'>$ ls /etc/ >ls.txt</code></p>

<p class="spip">Envoie la sortie standard à la fin du fichier ls.txt :</p>

<p class="spip"><code class='spip_code' dir='ltr'>$ ls /etc/ >>ls.txt</code></p>

<p class="spip">Envoie la sortie d’erreur dans le fichier ls.err :</p>

<p class="spip"><code class='spip_code' dir='ltr'>$ ls /toto 2>ls.err</code></p>

<p class="spip">Redirection de la sortie standard et de la sortie d’erreur dans deux fichiers séparés :</p>

<p class="spip"><code class='spip_code' dir='ltr'>$ ls /etc/ >ls.txt 2>ls.err</code></p>

<p class="spip">Envoie la sortie d’erreur et la sortie standard dans le fichier ls.txt :</p>

<p class="spip"><code class='spip_code' dir='ltr'>$ ls /toto 2>&1 >ls.txt</code></p>

<p class="spip">La même chose en plus court :</p>

<p class="spip"><code class='spip_code' dir='ltr'>$ ls /toto &>ls.txt</code></p>

<p class="spip">Redirige le résultat de deux commandes dans ls.txt</p>

<p class="spip"><code class='spip_code' dir='ltr'>(ls /etc; ls /toto) &>ls.txt</code></p>

<h2>Opérateur de test [ ]</h2>
<p class="spip">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 « <strong class="spip"> echo </strong> » seulement si le fichier existe :</p>

<p class="spip"><code class='spip_code' dir='ltr'>$ [ -f ./test.sh ] && echo "OK"</code></p>

<p class="spip">Arguments les plus courant de l’opérateur</p>

<table class="spip">
<tbody>
<tr class="row_even"><td>Option</td><td>Vrai si</td></tr>
<tr class="row_odd"><td>-e fichier</td><td>Le fichier indiqué existe</td></tr>
<tr class="row_even"><td>-d répertoire</td><td>Le répertoire indiqué existe</td></tr>
<tr class="row_odd"><td>-f fichier</td><td>Le fichier indiqué est un fichier régulier</td></tr>
<tr class="row_even"><td>-h fichier</td><td>Le fichier indiqué est un lien symbolique</td></tr>
<tr class="row_odd"><td>chaine1 = chaine2</td><td>Les deux chaînes sont identiques</td></tr>
<tr class="row_even"><td>val1 -eq val2</td><td>Les deux valeurs sont égales</td></tr>
<tr class="row_odd"><td>val1 -ge val2</td><td>val1 >= val2</td></tr>
<tr class="row_even"><td>val1 -gt val2</td><td>val1 > val2</td></tr>
<tr class="row_odd"><td>val1 -le val2</td><td>val1 <= val2</td></tr>
<tr class="row_even"><td>val1 -lt val2</td><td>val1 < val2</td></tr>
<tr class="row_odd"><td>val1 -ne val2</td><td>val1 != val2</td></tr>
</tbody>
</table>
<h2>Condition (if...else...fi)</h2>
<p class="spip">Syntaxe générale :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>if condition ; then<br />
  ...<br />
else<br />
  ...<br />
fi</code></div>
<p class="spip">Cette instruction s’utilise en générale avec l’opérateur de test détaillé au chapitre précédent. Exemple :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>if [ -f ./test.sh ] ; then <br />
  echo "OK"<br />
fi</code></div>
<p class="spip">Ou sur une seule ligne :</p>

<p class="spip"><code class='spip_code' dir='ltr'>if [ -f ./test.sh ] ; then echo "OK" ; fi</code></p>

<h2>Condition « case...esac »</h2>
<p class="spip">Syntaxe générale :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>case expression in<br />
  motif1) commande 1 ;;<br />
  motif2) commande 2 ;;<br />
  ...<br />
esac</code></div>
<p class="spip">Le motif peut contenir 
<br /></p>
<li> * : N’importe quelle chaîne de caractères
<br /><li>  ? : N’importe quel caractère
<br /><li> [abc] : Alternance de caractères
<br /><li> [A-Z] : Suite de caractères
<br /><li> | : OU permettant de combiner plusieurs motifs
<p class="spip">Exemple :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>#! /bin/bash<br />
<br />
while [ -n "$1" ]; do<br />
  ping -c 1 -w 1 $1 >/dev/null 2>&1<br />
  case $? in<br />
    0) echo -e "$1 \t OK !";;<br />
    1) echo -e "$1 \t injoignable !";;<br />
    2) echo -e "$1 \t inexistant !";;<br />
  esac<br />
  shift<br />
done</code></div>
<h2>Boucle « while...done »</h2>
<p class="spip">Syntaxe générale :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>while condition ; do<br />
  ...<br />
done</code></div>
<p class="spip">Exemple :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>i=0 ; <br />
while [ $i -lt 5 ] ; do<br />
  i=$((i+1))<br />
  echo $i<br />
done</code></div>
<p class="spip">La même chose sur une ligne :</p>

<p class="spip"><code class='spip_code' dir='ltr'>i=0 ; while [ $i -lt 5 ] ; do i=$((i+1)) ; echo $i ; done</code></p>

<p class="spip">Un autre exemple en utilisant l’instruction « <strong class="spip"> break </strong> »permettant de sortie de la boucle :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>i=0<br />
while true ; do<br />
        echo "$i avec break"<br />
        i=$((i+1))<br />
        [ $i -eq 5 ] && break<br />
done</code></div>
<h2>Boucle « until...done »</h2>
<p class="spip">Syntaxe générale :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>until condition ; do<br />
  ...<br />
done</code></div>
<h2>Boucle « for...done »</h2>
<p class="spip">Le fonctionnement de cette boucle est très différent de celui des autres langages.</p>

<p class="spip">Syntaxe :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>for variale in liste_de_mots ; do<br />
  ...<br />
done</code></div>
<p class="spip">Exemple :</p>

<p class="spip"><code class='spip_code' dir='ltr'>$ for var in a b c d ; do echo $var ; done</code></p>

<p class="spip">Renommer une série de fichiers :</p>

<p class="spip"><code class='spip_code' dir='ltr'>for i in *.tgz ; do mv $i ${i%tgz}tar.gz ; done</code></p>

<h2>Boucle « select...done » permet de réaliser des listes de choix</h2>
<p class="spip">Exemple :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>$ select var in a b c ; do echo $var ; done<br />
1) a<br />
2) b<br />
3) c<br />
#?       </code></div>
<h2>Fonctions</h2>
<p class="spip">Voici la syntaxe générale d’une fonction :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>function nom_de_la_fonction ()<br />
{<br />
...<br />
}</code></div>
<h2>Passage d’arguments</h2>
<p class="spip">$0 : Contient le nom du script</p>

<p class="spip">$1 : Premier argument ($2 : Deuxième argument,..)</p>

<p class="spip">$<i class="spip">10</i> : Dixième argument (Attention : $10 retourne le premier argument suivi de 0</p>

<p class="spip">$* :  Tous les arguments mais sans protéger les espaces et caractères spéciaux</p>

<p class="spip">"$@" : Tous les arguments mais en protégeant les espaces et caractères spéciaux</p>

<p class="spip">$# : Nombre d’arguments de la fonction</p>

<p class="spip">La commande « <strong class="spip"> shift </strong> » 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 :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>while [ -n "$1" ] ; do<br />
        echo $1<br />
        shift<br />
done</code></div>
<p class="spip">Affiche la lites des arguments passés au script :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>for i in "$@" ; do<br />
  echo $i<br />
done</code></div>
<p class="spip">La même chose en plus court :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>for i ; do<br />
  echo $i<br />
done</code></div>
<h2>Traitement des options dans un script</h2>
<h3>Traitement des options sans arguments</h3>
<p class="spip">L’instruction « <strong class="spip"> getopts </strong> » 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.</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>while getopts "abc" option ; do<br />
  case $option in<br />
    a) echo "Option a";;<br />
    b) echo "Option b";;<br />
    c) echo "Option c";;<br />
  esac<br />
done</code></div>
<p class="spip">Test :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>$ ./traite_arguments.sh -c -ab<br />
Option c<br />
Option a<br />
Option b</code></div>
<h3>Traitement des options avec argument</h3>
<p class="spip">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 :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>while getopts "abc:d:" option ; do<br />
  case $option in<br />
    a) echo "Option a";;<br />
    b) echo "Option b";;<br />
    c) echo "Option c, argument $OPTARG";;<br />
    d) echo "Option d, argument $OPTARG";;<br />
  esac<br />
done</code></div>
<p class="spip">Test :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>$ ./traite_arguments2.sh -c toto -ab -d tutu<br />
Option c, argument toto<br />
Option a<br />
Option b<br />
Option d, argument tutu</code></div>
<h3>Traitement des erreurs</h3>
<p class="spip">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 « <strong class="spip"> getopts </strong> » retourne «  ? » qu’il suffit de traiter dans le script :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>while getopts ":abc:d:" option ; do<br />
  case $option in<br />
    a) echo "Option a";;<br />
    b) echo "Option b";;<br />
    c) echo "Option c, argument $OPTARG";;<br />
    d) echo "Option d, argument $OPTARG";;<br />
    :) echo "Argument manquant pour l'option -$OPTARG";;<br />
    ?) echo "Option -$OPTARG inconnue";;<br />
  esac<br />
done</code></div>
<h3>Traitement des options longues sans arguments</h3>
<p class="spip">Par défaut  « <strong class="spip"> getopts </strong> » 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.</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>while getopts ":hv-:" option ; do<br />
  if [ "$option" = "-" ] ; then<br />
    case $OPTARG in<br />
      help)    option=h;;<br />
      version) option=v;;<br />
      *)       echo "Option $OPTARG inconnue";;<br />
    esac<br />
  fi<br />
  case $option in<br />
    h) echo "Option h";;<br />
    v) echo "Option v";;<br />
    ?) echo "Option -$OPTARG inconnue";;<br />
  esac<br />
done</code></div>
<p class="spip">Test :</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code>$ ./traite_arguments3.sh --version -h -a<br />
Option v<br />
Option h<br />
Option -a inconnue</code></div>
<h2>Pour avoir plus d’informations</h2>
<p class="spip">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 :
<br /></p>
<li> <a href="http://www.editions-eyrolles.com/Livre/9782212110289/langages-de-scripts-sous-linux" class="spip_out">http://www.editions-eyrolles.com/Livre/9782212110289/langages-de-scripts-sous-linux</a>
<h2>Historique des modifications</h2>
<table class="spip">
<thead><tr class='row_first'><th scope='col'><strong class="spip">Version</strong></th><th scope='col'><strong class="spip">Date</strong></th><th scope='col'><strong class="spip">Commentaire</strong></th></tr></thead>
<tbody>
<tr class="row_even"><td>0.1</td><td>14/08/08</td><td>Création par Tony GALMICHE</td></tr>
<tr class="row_odd"><td> </td><td> </td><td> </td></tr>
</tbody>
</table>