Chapitre 14. Substitution de commandes

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

La forme classique de la substitution de commande utilise l'apostrophe inverse (`...`). 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`   # << nomfichier >> 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 est              xargs rm -- < nomfichier 
#  ( -- couvre les cas dans lesquels  << nomfichier >> commence par un
#+ << - >> )

listing_fichierstexte=`ls *.txt`
#  Cette variable contient les noms de tous les fichiers *.txt dans le 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 advenir lorsque l'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 d'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.

Attention

Les substitutions de commandes peuvent provoquer des coupures de mots.
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 s'il n'y a pas coupure de mots, une substitution de commandes peut ôter 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 tel 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 est  "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

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 file2`   # Affecte à "variable2" le contenu de "fichier2".

#  Soyez conscient que ces variables peuvent alors 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

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 14-1. Trucs de script stupides

#!/bin/bash
# stupid-script-tricks.sh: Ne tentez pas ça chez vous, les gars!
# D'après "Trucs de Scripts Stupides," Volume I.


variable_dangereuse=`cat /boot/vmlinuz`   # Le noyau Linux compressé en personne.

echo "longueur de la chaîne  \$variable_dangereuse = ${#variable_dangereuse}"
# longueur de la chaîne  $variable_dangereuse = 794151
# (Ne donne pas le même résultat que  'wc -c /boot/vmlinuz'.)

# echo "$variable_dangereuse"
# N'essayez pas de faire ça! Cela figerait le script.


#  L'auteur de ce document n'a pas connaissance d'une l'utilité quelconque pour
#+ l'affectation à une variable du contenu d'un fichier binaire.

exit 0

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

Une 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 14-2. Générer le contenu d'une variable à partir d'une boucle

#!/bin/bash
# csubloop.sh: Initialiser une variable à la sortie d'une boucle.

variable1=`for i in 1 2 3 4 5
do
  echo -n "$i"                 #  La commande 'echo' est essentielle
done`                          #+ à la substitution de commande.

echo "variable1 = $variable1"  # variable1 = 12345


i=0
variable2=`while [ "$i" -lt 10 ]
do
  echo -n "$i"                 # A nouveau le nécessaire 'echo'.
  let "i += 1"                 # Incrémentation.
done`

echo "variable2 = $variable2"  # variable2 = 0123456789

exit 0

Note

La syntaxe $(COMMANDE) a remplacé les apostrophes inverses pour la substitution de commande.
sortie=$(sed -n /"$1"/p $fichier)
# Tiré de l'exemple "grp.sh".

Exemples de substitution de commandes dans des scripts shell :

  1. Exemple 10-7

  2. Exemple 10-26

  3. Exemple 9-26

  4. Exemple 12-2

  5. Exemple 12-15

  6. Exemple 12-12

  7. Exemple 12-39

  8. Exemple 10-13

  9. Exemple 10-10

  10. Exemple 12-24

  11. Exemple 16-7

  12. Exemple A-18

  13. Exemple 28-1

  14. Exemple 12-32

  15. Exemple 12-33

  16. Exemple 12-34

Notes

[1]

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.

>

Trent Fisher's groff tutorial.

Mark Komarinski's Printing-Usage HOWTO.

Rick Hohensee has written the osimpa i386 assembler entirely as Bash scripts.

Rocky Bernstein is in the process of developing a << full-fledged >> debugger for Bash.

---

The excellent "Bash Reference Manual", by Chet Ramey and Brian Fox, distributed as part of the "bash-2-doc" package (available as an rpm). See especially the instructive example scripts in this package.

The comp.os.unix.shell newsgroup.

The manpages for bash and bash2, date, expect, expr, find, grep, gzip, ln, patch, tar, tr, bc, xargs. The texinfo documentation on bash, dd, m4, gawk, and sed.

ALIGN="left" VALIGN="top" >PrécédentSommaireSuivantConstructeurs de listesNiveau supérieurFichiers