Table des matières
Le shell utilise différents caractères particuliers pour effectuer ses propres traitements ($ pour la substitution, > pour la redirection de la sortie standard, * comme caractère générique, etc.). Pour utiliser ces caractères particuliers comme de simples caractères, il est nécessaire de les protéger de l'interprétation du shell. Trois mécanismes sont utilisables :
Protection d'un caractère à l'aide du caractère \
Ce caractère protège le caractère qui suit immédiatement le caractère \.
$ ls tata toto $ echo * tata toto $ echo \* => le caractère * perd sa signification de caractère générique * $ $ echo \\ => le deuxième caractère \ perd sa signification de caractère de protection \ $ $
Le caractère \ permet également d’ôter la signification de la touche Entrée. Cela a pour effet d’aller à la ligne sans qu’il y ait exécution de la commande. En effet, après saisie d’une commande, l’utilisateur demande au shell l’exécution de celle-ci en appuyant sur cette touche. Annuler l’interprétation de la touche Entrée autorise l’utilisateur à écrire une longue commande sur plusieurs lignes.
Dans l’exemple ci-dessous, la signification de la touche Entrée a été inhibée par le caractère \ : bash détecte que la commande interne echo n’est pas terminée et affiche la chaîne d’appel contenue dans la variable prédéfinie PS2 du shell.
$ echo coucou \Entrée > salut Entrée => terminaison de la commande : le shell l’exécute ! coucou salut $
Protection partielle "chaîne"
A l'intérieur d'une paire de guillemets, tous les caractères de chaîne sauf $ \ ` " sont protégés de l'interprétation du shell. Cela signifie, par exemple, que le caractère $ sera quand même interprété comme une substitution à effectuer.
$ echo "< * $PWD ' >" < * /home/sanchis ' > $ $ echo "\"$PS2\"" "> " => valeur de la variable prédéfinie PS2 entre guillemets $
Protection totale 'chaîne'
Entre une paire d’apostrophes (′), aucun caractère de chaîne (sauf le caractère ') n'est interprété.
$ echo '< * $PWD * >' < * $PWD * > $
L’utilisation du caractère apostrophe peut provoquer l’erreur suivante.
$ echo c'est lundi > => le shell interprète l’apostrophe comme un début de chaîne à > ' => protéger et attend par conséquent la deuxième apostrophe cest lundi $
Pour éviter cela, on peut supprimer la signification particulière de cette apostrophe ou bien utiliser une paire de guillemets.
$ echo N\'oublie pas ! N'oublie pas ! $ $ echo "c'est lundi" c'est lundi $
Certaines commandes unix telles que find utilisent pour leur propre fonctionnement les mêmes caractères génériques que ceux du shell. Utilisés sans précaution, ces caractères provoquent le plus souvent une erreur d’exécution. Par exemple, la commande
find . -name *.py –print
recherche à partir du répertoire courant et affiche (ou plutôt devrait afficher) tous les noms d’entrées se terminant par la chaîne .py.
$ ls -l total 20 -rw-r--r-- 1 sanchis sanchis 954 27 mars 13:19 cliTCP.py drwxr-xr-x 2 sanchis sanchis 4096 27 mars 13:19 Divers -rwxr--r-- 1 sanchis sanchis 15 27 mars 13:20 ou -rwxr--r-- 1 sanchis sanchis 39 27 mars 13:20 ou0 -rw-r--r-- 1 sanchis sanchis 2208 27 mars 13:17 servTCP.py $ $ find . -name *.py -print find: paths must precede expression: `servTCP.py' find: possible unquoted pattern after predicate `-name'? $
L’origine du problème est que le caractère * aurait dû être interprété par la commande find alors qu’il a été interprété par bash, ce qui a conduit à l’exécution de la commande
find . -name cliTCP.py servTCP.py –print
La commande find signale une erreur de syntaxe car elle n’attend qu’une seule chaîne après son option –name.
Pour corriger le problème, il suffit de protéger le caractère * de l’interprétation de bash.
$ find . -name "*.py" -print ./cliTCP.py ./servTCP.py $
Dans ce cas de figure, le type de protection utilisé n’a pas d’importance : on aurait également pu écrire '*.py' ou \*.py.
La présence de caractères espace (ou de caractères spéciaux de bash) dans la valeur d’un paramètre (variable ou paramètre de position) peut poser problème lorsque cette valeur participe à l’exécution d’une commande.
Soit le programme shell nblig0 qui affiche le nombre de lignes (commande wc –l) d’un fichier dont le nom est saisi par l’utilisateur et contenu dans la variable rep.
#!/bin/bash # @(#) nblig0 read -p "Saisissez un nom d'entree : " rep wc -l $rep
$ ls -l total 12 -rw-r--r-- 1 sanchis sanchis 25 27 mars 13:35 'Meteo du jour.txt' -rwxr--r-- 1 sanchis sanchis 82 27 mars 13:37 nblig -rwxr--r-- 1 sanchis sanchis 81 27 mars 13:36 nblig0 $ $ nblig0 Saisissez un nom d'entree : Meteo du jour.txt wc: Meteo: Aucun fichier ou dossier de ce type wc: du: Aucun fichier ou dossier de ce type wc: jour.txt: Aucun fichier ou dossier de ce type 0 total $
La variable rep contient bien la chaîne Meteo du jour.txt mais après substitution de $rep, la commande qui sera finalement exécutée est : wc –l Meteo du jour.txt
La commande unix wc n’est pas exécutée avec un seul argument mais avec trois. Chacun d’eux est considéré par wc comme un nom de fichier qui, absent, provoque une erreur d’exécution.
La solution à ce problème consiste à préserver l’intégrité de la valeur de la variable rep après que la substitution ait été effectuée par le shell. On utilise pour cela la syntaxe "$rep". Il est à noter que la syntaxe '$rep' aurait été inadéquate car elle interdit au shell d’effectuer la substitution $rep.
$ cat nblig #!/bin/bash # @(#) nblig read -p "Saisissez un nom d'entree : " rep wc -l "$rep" $ $ nblig Saisissez un nom d'entree : Meteo du jour.txt 1 Meteo du jour.txt $
De manière générale, il est fortement conseillé de préserver l’intégrité d’une chaîne de caractères obtenue après une substitution, surtout lorsque l’on ne connait pas à l’avance la composition de cette chaîne.
Cette politique est systématiquement suivie dans l’écriture des programmes shell qui accompagnent l’interpréteur bash (ex : .bashrc, .profile).
$ cat ~/.profile # ~/.profile: executed by the command interpreter for login shells. # . . . # if running bash if [ -n "$BASH_VERSION" ]; then # include .bashrc if it exists if [ -f "$HOME/.bashrc" ]; then . "$HOME/.bashrc" fi fi # set PATH so it includes user's private bin if it exists #if [ -d "$HOME/bin" ] ; then # PATH="$HOME/bin:$PATH" #fi $
Les fichiers shell .bashrc et .profile permettent à l’utilisateur de configurer son environnement de travail [cf. Chapitre 1, Introduction à Bash, § 1.1]. Ces deux fichiers sont présents dans le répertoire de connexion de l’utilisateur dont le chemin peut être rapidement désigné par le caractère ~.
Avant qu’une syntaxe spécifique ne soit introduite dans bash, le mécanisme d’indirection [cf. Chapitre 2, Substitution de paramètres, § 6] était obtenu à l’aide de la commande interne eval et des caractères de protection du shell.
La syntaxe de cette commande interne est la suivante : eval [ arg ... ]
La commande interne eval interprète au sens du shell (substitution de paramètres, etc.) chacun de ses arguments arg puis concatène la chaîne résultante en une seule chaîne. Celle-ci est ensuite à nouveau interprétée et exécutée comme une commande par le shell. En d’autres termes, eval permet d’effectuer une interprétation en deux passes.
$ var=v1 $ v1=un $ eval echo \$$var un $
Lors de la première passe, le premier caractère $ n’est pas interprété car il est protégé de l’interprétation du shell (\$) et la chaîne $var est remplacée par la valeur de la variable var. A l’issue de la première interprétation, on obtient la chaîne suivante : echo $v1
Cette chaîne est ensuite interprétée comme commande par bash.
Une autre manière de procéder est la suivante :
$ var=\$v1 $ v1=un $ $ eval echo $var un $
Cette formulation permet de généraliser le mécanisme et d’obtenir une indirection multiniveau.
$ var=\$v1 $ v1=\$v2 $ v2=\$v3 $ v3=trois $ $ eval eval eval echo $var trois $
Les interprétations successives donnent les résultats suivants :
$ echo $var $v1 $ eval echo $var $v2 $ eval eval echo $var $v3 $ $ eval eval eval echo $var trois $