Gérer correctement les options de vos commandes

Menu principal

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.

Option ou paramètre

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 !

la position
la plus simple à mettre en œuvre mais limitant les possibilités. Elle est utilisée par des commandes comme tar. Ex. : dans la commande tar xzvf fichier.tar.gz le premier paramètre correspond aux options de tar (x=extract, z=gunzip, v=verbose, f=file)
le simple tiret
la plus répandue surtout sur les anciens systèmes, elle consiste à précéder d’un tiret une lettre représentant une option. Il est également possible avec cette convention de regrouper plusieurs options dans un seul argument. Par exemple la commande 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 commande
le double tiret
la convention du double tiret permet de corriger le manque de lisibilité du simple tiret. Souvent ces deux conventions coexistent. Avec cette convention, il n’est pas possible de combiner plusieurs options en une seule. Par contre, les options sont beaucoup plus parlantes. Par exemple, mkdir --parents --verobse est plus lisible que mkdir -pv.

Les substitutions du shell

Sous Unix, les langages de script shell opèrent des substitutions sur les commandes que vous tapez avant de les exécuter :

Ce sont ces substitutions qui créent des failles. Et on peut même les combiner !

La protection des variables

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 désactivation de l’interprétation des options

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".

printf vs echo

La commande echoest 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.

Récapitulatif

En résumé :