Les langages de script shell sous Unix sont des langages puissants. Ils proposent tellement de facilités qu’au final, si on ne fait pas attention, ces facilités se retournent contre vous.
Voici quelques règles et astuces pour vous éviter quelques nuits blanches à chercher pourquoi un script qui marchait parfaitement se met tout à coup à déconner.
Du point de vue d’Unix, une commande n’est rien d’autre qu’une liste d’arguments dont le premier représente l’exécutable. Les arguments ne sont que des chaînes de caractères.
Dans ce cas, comment reconnaître une option (qui définira le comportement de la commande) d’un paramètre (un nom de fichier, de répertoire etc.) ?
La distinction passe par une convention. Et il en existe plusieurs !
tar xzvf fichier.tar.gz
le premier paramètre correspond aux options de tar (x=extract, z=gunzip, v=verbose, f=file)ps -ef
est identique à la commande ps -e -f
. Pour passer des valeurs à ce genre d’options, il faut ajouter un argument supplémentaire. Par exemple, dans la commande bash -c "echo hello"
, la chaîne "echo hello"
est une valeur liée à l’option -c
. Cette convention présente des inconvénients de lisibilité du code produit. Il n’est en effet pas évident de connaître l’effet de l’option -e
de ps
sans regarder l’aide de cette commandemkdir --parents --verobse
est plus lisible que mkdir -pv
.Sous Unix, les langages de script shell opèrent des substitutions sur les commandes que vous tapez avant de les exécuter :
?
et *
)$variable
ou "Message $variable"
etc.)
$(echo "hello")
)Ce sont ces substitutions qui créent des failles. Et on peut même les combiner !
Veillez à toujours protéger vos variables en les entourant avec des doubles quotes ("
) car le moindre espace qu’elles pourraient contenir aurait des conséquences néfastes.
Donc $fichier
est à proscrire. Il faut préférer
commande "$fichier"
.
La substitution des variables n’est pas la seule chose à prendre en compte. Il faut également se méfier des variables quand la commande à exécuter supporte une grande variété d’options.
Par exemple, si la commande que vous exécutez esttouch "$fichier"
, cette dernière générera une erreur si la variable $fichier
contient la chaîne -f
(la commande touch
indiquera opérande fichier manquant
sous un Linux en français).
C’est tout à fait normal car, du point de vue de cette commande, vous n’avez fourni que l’option -f
, sans aucun fichier.
Pour remédier à cela, la plupart des commandes permettent de spécifier les options au début et de les séparer par un double tiret --
des paramètres.
Pour être la plus fiable possible, la commande aurait dû être écrite touch -- "$fichier"
.
La commande echo
est probablement la commande la plus utilisée dans les scripts shells. Elle pose néanmoins plusieurs problèmes.
Premièrement elle n’est pas standardisée. En fonction de votre OS ou du shell utilisé (Bash, CSH, KSH etc.), cette commande n’accepte pas les mêmes paramètres ni n’interprète les séquences d’échappement de la même façon.
Deuxièmement il n’est souvent pas possible de désactiver l’interprètation des options en utilisant le double tiret --
ce qui empêche l’affichage de certaines chaînes de caractères. Par exemple sous Bash, il n’est pas possible d’afficher les chaînes -e
et -E
car elles sont interprêtées comme des options.
La solution qui corrige ces problèmes consiste à utiliser la commandeprintf
. Sous Bash, cela n’a pas d’impact sur les performances car elle fait partie des commandes internes.
Pour émuler la commandeecho "$variable"
, il faut utiliser printf '%s\n' "$variable"
. Sous cette forme, la chaîne contenue dans la variable $variable
sera affichée telle qu’elle, quels que soient les caractères qu’elle pourrait contenir.
Je ne saurais donc trop vous recommander de remplacer ce pattern vu régulièrement : echo "$variable" | commande
par printf '%s' "$variable" | commande
.
En résumé :
"
,
--
pour toutes les commandes le supportant,
printf
à la place de echo
.