L'interprêteur bash

L'interprêteur de commande bash est l'un des nombreux interprêteurs de commande disponibles. Il est la plupart du temps présent par défaut dans les distributions Linux.

bash veut dire Bourne-Again Shell et se veut une écriture GNU du shell présent initialement sous Unix, le Bourne Shell nommé sh.

Lorsqu'on démarre une session sous Unix ou lorsqu'on ouvre un terminal, le système exécute l'interprêteur bash qui se met alors en attente d'instructions de l'utilisateur.

Scripts

Un script est un programme (bash). Il consiste à regrouper un ensemble de commande dans un fichier et à donner les droits en exécution de ce fichier.

La première ligne du script est une ligne particulière, elle indique au système d'exploitation qu'elle est l'interprêteur à utiliser. Dans ce cas, la ligne commence par #! suivi du chemin d'accès à l'interprêteur (nota: on peut l'obtenir en utilisant la commande which).

En bash, toute ligne contenant # n'est pas interprêté au-delà de ce #. # désigne le début d'un commentaire.

Une ligne de commande bash se termine soit par un retour chariot (\n), soit par un point-virgule (;). Si une commande doit continuer à la ligne suivante, on utilise le caractère de continuation \.

#!/bin/bash
echo Bonjour
# un commentaire
ls
echo a; echo b
echo bonjour\
le monde

Exemple:

cat > script1 << END
#!/bin/bash
echo Bonjour
END
chmod +x script1
./script1

Variables

Pendant l'exécution d'un programme, un processus doit garder en mémoire des valeurs. Pour cela, il utilise des variables.

Sous bash, l'affectation d'une valeur à une variable se fait grâce à l'opérateur =.

i="toto"
j=2
k=3.6

Pour obtenir la valeur d'un variable, il suffit d'utiliser le préfix $.

echo $i
echo $j
echo $k

Pour éviter la confusion dans les noms, il est préférable d'entourer le nom d'une variable par des accolades:

ij=314
echo $i
echo $j
echo $ij
echo $i$j
echo ${ij}
echo ${i}${j}
echo ${i}j

Par défaut, une variable non initialisée est vide:

echo $ijk

Variables d'environnement

Lorsqu'un processus père crée un processus fils, le premier transfère au second un certain nombre de variables appelées variables d'environnement. Cela permet au père de transmettre des valeurs qui seront connues par le processus fils.

Pour connaitre l'ensemble des variables d'environnement connues par le processus courant, on peut utiliser la commande env. Un certain nombre de ces variables sont d'une grande importance pour le fonctionnement de l'interpreteur de commande:

Pour créer une variable d'environnement, il suffit d'utiliser la commande export.

# sans export
i=toto
cat > f << END
#!/bin/bash
echo \$i
END
chmod +x f
./f
# avec export
export i=toto
./f

PWD

Print Working Directory

Cette variable contient le nom du répertoire courant.

OLDPWD

Cette variable contient le nom du répertoire précédent au répertoire courant.

HOME

Cette variable contient le nom du répertoire par défaut de l'utilisateur (son répertoire maison).

SHELL

Cette variable contient le nom de l'interpreteur de commande courant.

USER

Cette variable contient le nom (login) de l'utilisateur.

PATH

Cette variable contient la liste des répertoires pouvant contenir des commandes exécutables.

A chaque appel d'une commande, soit le chemin (absolu ou indirecte) de la commande est donné et alors l'interprêteur va chercher la commande dans le répertoire indiqué, l'interprêteur va voir dans les répertoires désigné par PATH s'il trouve la commande en question, s'il ne la trouve pas, il indique qu'il ne la pas trouvé (ce qui ne veut pas dire qu'elle n'existe pas !).

f
which f
./f
which f
echo $PATH
export PATH=$PATH:.
which f
f

La commande which permet de connaître où se trouve une commande.

which ls

Si une commande est présente à plusieurs endroits, c'est le premier répertoire du PATH qui prime. L'option -a de which permet de savoir si la commande est présente en plusieurs endroits.

LD_LIBRARY_PATH

De manière équivalente à PATH, LD_LIBRARY_PATH indique à l'interprêteur où chercher des bibliothéques logicielles. Cela est important pour les langages compilés qui utilisent des bibliothèques partagées.

La commande ldd permet de lister pour une commande compilée l'ensemble des bibliothèques utilisées. Au chargement de la commande, si une bibliothèque manque, la commande ne sera pas exécutée. Il faut alors modifier LD_LIBRARY_PATH en conséquence.

which ls
ldd /bin/ls

$

Chaque processus est repéré par un identifiant (PID) unique, le numéro de cet identifiant peut-être obtenu par la variable $

echo $$

#, @, 1, 2, 3, ...

Un script shell peut admettre des arguments. Ils sont placés dans une liste d'arguments accessibles par des variables.

variable signification
$# nombre d'arguments du script
$@ les arguments du script (tous)
$0 le nom du script
$1 le premier argument du script
$2 le deuxième argument du script
$n le n-ième argument du script

La commande shift sert à décrémenter de 1 la liste des arguments d'un script.

Exemple de script:

script.sh

#!/bin/bash

echo premier argument: $1
echo deuxieme argument: $2
echo troisieme argument: $3
echo shift
shift
echo premier argument: $1
echo deuxieme argument: $2
#utilisation du script
./script.sh a1 a2 a3

Utilisation des quotes

Les quotes simples ('...') ou doubles ("...") ont une signification particulière.

Les quotes doubles permettent de regrouper un ensemble de mots (séparés par des espaces) en un seul. Le shell contenu cependant à interprêter les caractères spéciaux ($, *, etc.).

Les quotes simples permettent de regrouper un ensemble de mots sans interprêtation par le shell.

Exemple:

script.sh

#!/bin/bash

i=toto
echo  $i         j
echo "$i         j"
echo '$i         j'

Les quotes "inversées" (`...`) permettent de faire exécuter une commande dans un processus fils.

Exemple:

i=toto.com
j=`basename $i .com`
echo avec suffixe: $i
echo sans suffixe: $j

les processus et bash

# numéro de processus courant
echo $$

script.sh

#!/bin/bash

echo $$
# execution d'un script
./script.sh

# execution du script dans le shell pere
. ./script.sh
# creation d'un script avec cat
cat > f << END
#!/bin/bash
echo $$
END

chmod +x f
./f
cat f

# correction du script
cat > f << END
#!/bin/bash
echo \$\$
END

chmod +x f
cat f
./f

tests et expressions conditionnelles

La commande test permet de tester une condition. Le code de retour vaut zéro si la condition est vrai, 1 sinon.

conditions signification
-e file vrai si file existe
-d file vrai si file existe et est un répertoire
-r file vrai si file existe et l'accès en lecture est vrai
-w file vrai si file existe et l'accès en écriture est vrai
-x file vrai si file existe et l'accès en exécution est vrai
file1 -nt file2 vrai si file1 est plus récent (date de modification) que file2
file1 -ot file2 vrai si file1 est plus ancien (date de modification) que file2
string1 == string2 vrai si la chaine string1 est identique à la chaine string2
string1 != string2 vrai si la chaine string1 est différente de la chaine string2
#!/bin/bash
cat > f1 << END
toto
END
\rm -f f2

test -e f1
echo $?
test -e f2 || echo "f2 n'existe pas"
cp f1 f2
test f1 -nt f2 || echo f1 plus recent
test f1 -ot f2 || echo f1 plus ancien

if (branchage)

bash fournit un mécanisme de branchage: if. Une condition est testée, si elle est vrai alors on applique les commandes exprimés après un then, sinon on applique les commandes exprimés après un else (facultatif).

Exemple:

if test f1 -nt f2
then
  echo f1 est plus recent
else
  echo f2 est plus recent
fi

Plusieurs branchages peuvent être imbriqués en utilisant elif (else if)

if test ...
then
 ...
elif test ...
then 
 ...
elif test ...
then
 ...
else
 ...
fi

for .. in .. (boucle)

Il est possible en bash d'itérer une liste élément par élément grâce à l'instruction for .. in ...

usage de for

for variable in liste
do
 ...
done

Exemple:

for i in a b c d e 
do
  echo $i
done

L'expansion shell peut être utilisé après le in.

for i in f*
do
  echo "un fichier commencant par f: $i"
done

case (sélection)

bash fournit également un moyen d'effectuer une série de tests sur une même variable, c'est-à-dire de choisir un cas parmi une liste et effectuer les commande afférentes.

Exemple:

#!/bin/bash

case $1 in 
 ( "toto" )
   echo toto
   ;;
 ( "titi" )
   echo titi
   ;&
 ( "tata" | "totoro" )
   echo zouh
   ;;
 ( * )
   echo pas reconnu
esac

;; arrête l'interprêtation des commandes, alors que ;& autorise la continuation de la lecture du script.