11. Substitution de commandes

Une substitution de commande réassigne la sortie d'une commande [47] ou même de multiples commandes ; elle branche littéralement la sortie d'une commande sur un autre contexte. [48]

La forme classique de la substitution de commande utilise les apostrophes inverses (`...`). Les commandes placées à l'intérieur de ces apostrophes inverses génèrent du texte en ligne de commande.

nom_du_script=`basename $0`
echo "Le nom de ce script est $nom_du_script."

La sortie des commandes peut être utilisée comme argument d'une autre commande, pour affecter une variable, voire pour génerer la liste des arguments dans une boucle for

rm `cat nomfichier`   # <quote>nomfichier</quote> contient une liste de fichiers à effacer.
#
#  S. C. fait remarquer qu'une erreur "arg list too long" (liste d'arguments
#+ trop longue) pourrait en résulter.
#  Mieux encore              xargs rm -- < nomfichier
#  ( -- couvre les cas dans lesquels  <quote>nomfichier</quote> commence par un
#+ <quote>-</quote> )

listing_fichierstexte=`ls *.txt`
#  Cette variable contient les noms de tous les fichiers *.txt
#+ du répertoire de travail actuel.
echo $listing_fichierstexte

listing_fichierstexte2=$(ls *.txt)   #  La forme alternative d'une substitution
                                     #+ de commande.
echo $listing_fichierstexte2
# Même résultat.

#  Un problème qui peut survenir lorsqu'on place une liste de fichiers dans
#+ une chaîne simple est qu'une nouvelle ligne peut s'y glisser.
#  Une méthode plus sûre pour assigner une liste de fichiers à un paramètre est
#+ d'utiliser un tableau.
#      shopt -s nullglob    #  S'il n'y a pas de correspondance, les noms de
#+                          #+ fichier sont transformés en chaîne vide.
#      listing_fichierstextes=( *.txt )
#
# Merci, S.C.
[Note]

Note

La substitution de commande appelle un sous-shell.

[Attention]

Attention

Les substitutions de commandes peuvent provoquer des coupures de mot.

COMMANDE `echo a b`     # 2 arguments: a et b

COMMANDE "`echo a b`"   # 1 argument : "a b"

COMMANDE `echo`         # pas d'argument

COMMANDE "`echo`"       # un argument vide


# Merci, S.C.

Même sans coupure de mots, une substitution de commande peut couper les retours à la ligne finaux.

# cd "`pwd`"  # Ceci devrait toujours fonctionner.
# Néanmoins...

mkdir 'répertoire avec un retour à la ligne final
'

cd 'répertoire avec un retour à la ligne final
'

cd "`pwd`"  # Message d'erreur:
#  bash: cd: /tmp/fichier avec un retour à la ligne final : Pas de fichier
#+ ou répertoire

cd "$PWD"   # Fonctionne parfaitement.





ancien_parametrage_tty=$(stty -g)   # Sauve les anciens paramètres du terminal.
echo "Appuyez sur une touche "
stty -icanon -echo           # Désactive le mode "canonique" du terminal.
                             # Désactive également l'écho *local* .
touche=$(dd bs=1 count=1 2> /dev/null)   #  Utilisation de dd pour obtenir
                                            #+ l'appui d'une touche.
stty "$ancien_parametrage_tty"      # Restaure les anciens paramètres.
echo "Vous avez appuyé sur ${#touche} touche."  # ${#variable} =  $variable
#
#  Appuyez sur toute autre touche que  RETURN, et la sortie devient  "Vous avez
#+ appuyé sur 1 touche"
# Appuyez sur  RETURN, et c'est  "Vous avez appuyé sur 0 touche."
# Le retour à la ligne a été avalé par la substitution de commande.

Merci, S.C.
[Attention]

Attention

L'utilisation d'echo pour afficher la valeur d'une variable non protégée affectée à l'aide d'une substitution de commande retire les caractères de nouvelle ligne finaux de la sortie des commandes ainsi redirigées, ce qui peut créer des surprises désagréables.

listing_rep=`ls -l`
echo $listing_rep     # non protégée

# Dans l'attente de la liste bien ordonnée du contenu d'un répertoire.

# En fait, voici ce que l'on obtient:
# total 3 -rw-rw-r-- 1 bozo bozo 30 May 13 17:15 1.txt -rw-rw-r-- 1 bozo
# bozo 51 May 15 20:57 t2.sh -rwxr-xr-x 1 bozo bozo 217 Mar 5 21:13 wi.sh

# Les retours à la ligne ont disparu.


echo "$listing_rep"   # protégée
# -rw-rw-r--    1 bozo       30 May 13 17:15 1.txt
# -rw-rw-r--    1 bozo       51 May 15 20:57 t2.sh
# -rwxr-xr-x    1 bozo      217 Mar  5 21:13 wi.sh

La substitution de commande permet même d'affecter à une variable le contenu d'un fichier, en utilisant soit une redirection soit la commande cat

variable1=`<fichier1`     # Affecte à "variable1" le contenu de "fichier1".
variable2=`cat fichier2`  # Affecte à "variable2" le contenu de "fichier2".
                          #  Néanmoins, ceci lance un nouveau processus,
                          #+ donc la ligne de code s'exécute plus lentement que
                          #+ la version ci-dessus.

# Note :
#  Les variables peuvent contenir des espaces,
#+ voire même (horreur), des caractères de contrôle.
#  Extraits des fichiers système, /etc/rc.d/rc.sysinit
#+ (sur une installation Red Hat Linux)


if [ -f /fsckoptions ]; then
        fsckoptions=`cat /fsckoptions`
...
fi
#
#
if [ -e "/proc/ide/${disk[$device]}/media" ] ; then
             hdmedia=`cat /proc/ide/${disk[$device]}/media`
...
fi
#
#
if [ ! -n "`uname -r | grep -- "-"`" ]; then
       ktag="`cat /proc/version`"
...
fi
#
#
if [ $usb = "1" ]; then
    sleep 5
    mouseoutput=`cat /proc/bus/usb/devices 2>/dev/null|grep -E "^I.*Cls=03.*Prot=02"`
    kbdoutput=`cat /proc/bus/usb/devices 2>/dev/null|grep -E "^I.*Cls=03.*Prot=01"`
...
fi
[Attention]

Attention

Ne pas affecter le contenu d'un gros fichier texte à une variable à moins que vous n'ayez une bonne raison de le faire. Ne pas affecter le contenu d'un fichier binaire à une variable, même pour blaguer.

Exemple 11.1. Trucs de script stupides

&stupscr;

Notez qu'on ne provoque pas de surcharge de tampon. C'est un exemple où un langage interprété, tel que Bash, fournit plus de protection vis à vis des erreurs de programmation qu'un langage compilé.

La substitution de commande permet d'affecter à une variable la sortie d'une boucle. L'idée pour y parvenir est de se servir de la sortie d'une commande echo placée à l'intérieur de la boucle.

Exemple 11.2. Générer le contenu d'une variable à partir d'une boucle

&csubloop;

[Note]

Note

La syntaxe $(...) a remplacé les apostrophes inverses pour la substitution de commande.

sortie=$(sed -n /"$1"/p $fichier) # Tiré de l'exemple "grp.sh".

# Initialiser une variable avec le contenu d'un fichier texte.
Contenu_fichier1=$(cat $fichier1)
Contenu_fichier2=$(<$fichier2)        # Bash le permet aussi.

La forme $(...) de la substitution de commande traite les doubles antislash d'une façon différente que `...`.

bash$ echo `echo \\`


bash$ echo $(echo \\)
\
              

Dans sa forme $(...), la substitution de commande autorise les imbrications. [49]

word_count=$( wc -w $(echo * | awk '{print $8}') )

ou quelque chose d'un peu plus élaboré...

Exemple 11.3. Découvrir des anagrammes

&agram2;

Exemples de substitution de commandes dans des scripts shell :

  1. Exemple 10.7, « Un remplaçant de grep pour les fichiers binaires »

  2. Exemple 10.26, « Utiliser la substitution de commandes pour générer la variable case »

  3. Exemple 9.31, « Réinitialiser RANDOM »

  4. Exemple 15.3, « Badname élimine dans le répertoire courant les fichiers dont le nom contient des caractères incorrects et des espaces blancs. »

  5. Exemple 15.22, « lowercase : Change tous les noms de fichier du répertoire courant en minuscule. »

  6. Exemple 15.17, « Émuler grep dans un script »

  7. Exemple 15.54, « Utiliser seq pour générer l'incrément d'une boucle »

  8. Exemple 10.13, « Utiliser efax en mode batch »

  9. Exemple 10.10, « Afficher les liens symboliques dans un répertoire »

  10. Exemple 15.32, « Supprimer les commentaires des programmes C »

  11. Exemple 19.8, « Boucle for redirigée »

  12. Exemple A.16, « tree: Afficher l'arborescence d'un répertoire »

  13. Exemple 27.3, « Trouver le processus associé à un PID »

  14. Exemple 15.47, « Paiement mensuel sur une hypothèque »

  15. Exemple 15.48, « Conversion de base »

  16. Exemple 15.49, « Appeler bc en utilisant un document en ligne »



[47] Dans le cadre des substitutions de commande, une commande peut être une commande système externe, une commande intégrée du shell voire même une fonction d'un script.

[48] Sur le plan technique, la substitution de commandes extrait la sortie (stdout) d'une commande et l'affecte à une variable en utilisant l'opérateur =.

[49] En fait, l'imbrication est aussi possible avec des guillemets inversés mais seulement en 'échappant' les guillemets inversés interne comme l'indique John Default.

nb_mots=` wc -w \`echo * | awk '{print $8}'\` `