10.2. Remplacement de paramètres

Manipuler et/ou étendre les variables

${parametre}

Identique à $parametre, c'est-à-dire la valeur de la variable parametre. Dans certains contextes, seule la forme la moins ambiguë, ${parametre}, fonctionne.

Peut être utilisé pour concaténer des variables avec des suites de caractères (strings).

votre_id=${USER}-sur-${HOSTNAME}
echo "$votre_id"
#
echo "Ancien \$PATH = $PATH"
PATH=${PATH}:/opt/bin  # Ajoute /opt/bin à $PATH pour la durée du script.
echo "Nouveau \$PATH = $PATH"
${parametre-defaut}, ${parametre:-defaut}
var1=1
var2=2
# var3 n'est pas initialisée.

echo ${var1-$var2}   # 1
echo ${var3-$var2}   # 2
            ^          Remarquez le préfixe $.


echo ${nom_utilisateur-`whoami`}
#  Affiche le résultat de `whoami` si la variable $nom_utilisateur n'est
#+ pas encore initialisée.
[Note]

Note

${parametre-defaut} et ${parametre:-defaut} sont pratiquement équivalents. Le caractère : supplémentaire est utile seulement lorsque parametre a été déclaré à la valeur nulle.

#!/bin/bash
# param-sub.sh

#  Qu'une variable ait été déclarée ou non
#+ a un effet sur le déclenchement de l'option par défaut,
#+ y compris si la variable est nulle.

nomutilisateur0=
echo "nomutilisateur0 a été déclaré mais laissé sans valeur."
echo "nomutilisateur0 = ${nomutilisateur0-`whoami`}"
# Rien ne s'affiche.

echo

echo "nomutilisateur1 n'a pas été déclaré."
echo "nomutilisateur1 = ${nomutilisateur1-`whoami`}"
# S'affiche.

nomutilisateur2=
echo "nomutilisateur2 a été déclaré mais laissé sans valeur."
echo "nomutilisateur2 = ${nomutilisateur2:-`whoami`}"
#                                        ^
# S'affiche à cause du :- au lieu du simple - dans le test conditionnel.
# Comparez avec le premier exemple ci-dessus.

#

# Je répète :

variable=
# variable a été déclarée mais est initialisée à null.

echo "${variable-0}"    # (pas de sortie)
echo "${variable:-1}"   # 1
#               ^

unset variable

echo "${variable-2}"    # 2
echo "${variable:-3}"   # 3

exit 0

Cette syntaxe de paramètre par défaut sert notamment, dans les scripts, à renseigner les arguments « absents » de la ligne de commande.

NOM_FICHIER_PAR_DEFAUT=donnees.generiques
nom_fichier=${1:-$NOM_FICHIER_PAR_DEFAUT}
#  Si rien n'est spécifié, l'ensemble de commandes suivant opère sur le
#+ fichier "donnees.generiques".
#  Debut-Bloc-Commandes
#  ...
#  ...
#  ...
#  Fin-Bloc-Commandes


#  Exemple tiré de "hanoi2.bash" :
DISQUES=${1:-E_NOPARAM}   # Il faut préciser le nombre de disques
#  Affecter la valeur $1 (le paramètre de ligne de commande) à
#+ $DISQUES, ou $E_NOPARAM si $1 n'est pas
#+ initialisée.

Voir aussi l'Exemple 3.4, « Sauvegarde de tous les fichiers modifiés depuis 24 heures », l'Exemple 31.2, « Créer un fichier de swap en utilisant /dev/zero » et l'Exemple A.6, « Séries de Collatz ».

Comparez cette méthode avec l'utilisation d'une liste ET pour fournir un argument par défaut à la ligne de commande.

${parametre=defaut}, ${parametre:=defaut}

Si le paramètre n'est pas initialisé, alors initialisation à la valeur par défaut.

Les deux formes sont pratiquement équivalentes. Le caractère : fait une différence seulement lorsque $parametre a été déclaré nul, [50] comme ci-dessus.

echo ${var=abc}   # abc
echo ${var=xyz}   # abc
# $var avait déjà été initialisée à abc, donc pas de changement.
${parametre+valeur_alt}, ${parametre:+valeur_alt}

Si le paramètre est déclaré, utilisez valeur_alt, sinon utilisez la chaîne de caractères vide.

Les deux formes sont pratiquement équivalentes. Le caractère : fait la différence seulement lorsque parametre a été déclaré nul, voir ci-dessous.

echo "###### \${parametre+valeur_alt} ########"
echo

a=${param1+xyz}
echo "a = $a"      # a =

param2=
a=${param2+xyz}
echo "a = $a"      # a = xyz

param3=123
a=${param3+xyz}
echo "a = $a"      # a = xyz

echo
echo "###### \${parametre:+valeur_alt} ########"
echo

a=${param4:+xyz}
echo "a = $a"      # a =

param5=
a=${param5:+xyz}
echo "a = $a"      # a =
# Résultat différent de a=${param5+xyz}

param6=123
a=${param6:+xyz}
echo "a = $a"      # a = xyz
${parametre?msg_err}, ${parametre:?msg_err}

Si le paramètre est initialisé, l'utiliser, sinon afficher msg_err et interrompre le script avec un code de sortie de 1.

Ces deux formes sont pratiquement équivalentes. Le caractère : fait la différence seulement lorsque parametre a été déclaré nul, comme ci-dessus.

Exemple 10.7. Utiliser le remplacement et les messages d'erreur

#!/bin/bash

# Vérifier certaines des variables d'environnements du système.
# C'est une mesure adéquate de maintenance préventive.
#  Si, par exemple, $USER, le nom de la personne sur la console, n'est pas
#+ initialisé, la machine ne vous reconnaîtra pas.

: ${HOSTNAME?} ${USER?} ${HOME?} ${MAIL?}
  echo
  echo "Le nom de la machine est $HOSTNAME."
  echo "Vous êtes $USER."
  echo "Votre répertoire personnel est $HOME."
  echo "Votre courrier est situé dans $MAIL."
  echo
  echo "Si vous lisez ce message, les variables d'environnement "
  echo "critiques ont été initialisées."
  echo
  echo

# ------------------------------------------------------

#  L'expression ${variablename?} peut aussi vérifier les
#+ variables configurées dans un script.

CetteVariable=Valeur-de-CetteVariable
#  Notez que, du coup, les variables chaînes de caractères pourraient être
#+ configurées avec les caractères contenus dans leurs noms.
: ${CetteVariable?}
echo "La valeur de CetteVariable est $CetteVariable".
echo
echo


: ${ZZXy23AB?"ZZXy23AB n'a pas été initialisée."}
#  Si ZZXy23AB n'a pas été initialisée, alors le script se termine avec un
#+ message d'erreur.

# Vous pouvez spécifier le message d'erreur.
# : ${nomvariable?"MESSAGE D'ERREUR."}


# Même résultat avec :  variable_stupide=${ZZXy23AB?}
#                      variable_stupide=${ZZXy23AB?"ZXy23AB n'a pas été initialisée."}
#
#                      echo ${ZZXy23AB?} >/dev/null

#  Comparez ces méthodes de vérification sur l'initialisation d'une variable
#+ avec "set -u" ...



echo "Vous ne verrez pas ce message parce que le script s'est déjà terminé"

ICI=0
exit $ICI   # Ne sortira *pas* ici.

# En fait, ce script quittera avec un code de sortie 1 (echo $?).

Exemple 10.8. Remplacement de paramètres et messages d'« usage »

#!/bin/bash
# usage-message.sh

: ${1?"Usage: $0 ARGUMENT"}
#  Le script sort ici si le paramètre en ligne de commande est absent,
#+ avec le message d'erreur suivant.
#    usage-message.sh: 1: Usage: usage-message.sh ARGUMENT

echo "Ces deux lignes ne s'affichent que si un paramètre en ligne de commande est donné."
echo "paramètre en ligne de commande = \"$1\""

exit 0 # Sortira ici seulement si un paramètre en ligne de commande est présent.

#  Vérifiez le code de sortie, avec ou sans paramètre en ligne de
#+ commande.
# Si un paramètre en ligne de commande est présent, alors "$?" vaut 0.
# Sinon, "$?" vaut 1.

Remplacement, expansion de paramètres. Les expressions suivantes sont le complément des opérations sur les suites de caractères comme match dans expr (voir l'Exemple 16.9, « Utiliser expr »). Ces derniers sont utilisés principalement pour analyser les chemins de fichiers.

Longueur de variables / Suppression d'un sous-ensemble d'une suite de caractères

${#var}

Longueur de la suite de caractères (ou nombre de caractères dans $var). Pour un tableau, ${#tableau} est la longueur du premier élément dans le tableau.

[Note]

Note

Exceptions :

  • ${#*} et ${#@} donnent le nombre de paramètres de position.

  • Pour un tableau, ${#tableau[*]} et ${#tableau[@]} donnent le nombre d'éléments dans le tableau.

Exemple 10.9. Longueur d'une variable

#!/bin/bash
# length.sh

E_SANS_ARGS=65

if [ $# -eq 0 ]  # Doit avoir des arguments en ligne de commande.
then
  echo "Merci d'appeler ce script avec un ou plusieurs argument(s) en ligne de commande."
  exit $E_SANS_ARGS
fi  

var01=abcdEFGH28ij
echo "var01 = ${var01}"
echo "Longueur de var01 = ${#var01}"
# Maintenant, essayons d'intégrer un espace.
var02="abcd EFGH28ij"
echo "var02 = ${var02}"
echo "Longueur de var02 = ${#var02}"

echo "Nombre d'arguments en ligne de commande passés au script = ${#@}"
echo "Nombre d'arguments en ligne de commande passés au script = ${#*}"

exit 0

${var#Motif}, ${var##Motif}

${var#Motif} Supprime de $var la partie la plus courte de $Motif correspondant au début de $var.

${var##Motif} Supprime de $var la partie la plus longue de $Motif correspondant au début de $var.

Un exemple d'utilisation à partir de l'Exemple A.7, « days-between : Calculer le nombre de jours entre deux dates » :

# Fonction provenant de l'exemple "days-between.sh"
# Supprimer le(s) zéro(s) du début à partir de l'argument donné.

supprimer_les_zeros_du_debut () # Supprime les zéros éventuels du début
{                     # à partir des arguments donnés.
  return=${1#0}       # Le "1" correspond à "$1", argument fourni.
                      # Le "0" correspond à ce qui doit être supprimé de "$1".
}

Une version plus élaborée par Manfred Schwarb :

supprimer_les_zeros_du_debut_2 () # Supprimer les zéros du début, car sinon
{                      # Bash interprétera de tels numéros en valeurs octales.
  shopt -s extglob     # Active le globbing local.
  local val=${1##+(0)} # Utilise une variable locale et fait correspondre la
                       # suite de zéros la plus longue.
  shopt -u extglob     # Désactive le globbing local.
  _strip_leading_zero2=${val:-0}
                       # Si l'entrée était 0, renvoie 0 au lieu de "".
}

Autre exemple d'utilisation :

echo `basename $PWD`        # Base du nom du répertoire courant.
echo "${PWD##*/}"           # Nom de base du répertoire de travail.
echo
echo `basename $0`          # Nom du script.
echo $0                     # Nom du script.
echo "${0##*/}"             # Nom du script.
echo
filename=test.data
echo "${filename##*.}"      # data
                            # Extension du fichier.
${var%Motif}, ${var%%Motif}

${var%Motif} Supprime de $var la partie la plus courte de $Motif qui s'ajuste à la fin de $var.

${var%%Motif} Supprime de $var la partie la plus longue de $Motif qui s'ajuste à la fin de $var.

Dans la version 2 de Bash, on a ajouté des options supplémentaires.

Exemple 10.10. Correspondance de motifs dans le remplacement de paramètres

#!/bin/bash
# patt-matching.sh

# Reconnaissance de motifs avec les opérateurs de substitution # ## % %%

var1=abcd12345abc6789
motif1=a*c  # * (joker) recherche tout ce qui se trouve entre a et c.

echo
echo "var1 = $var1"           # abcd12345abc6789
echo "var1 = ${var1}"         # abcd12345abc6789   (autre forme)
echo "Nombre de caractères dans ${var1} = ${#var1}"
echo

echo "motif1 = $motif1"     # a*c  (tout entre 'a' et 'c')
echo "------------------"
echo '${var1#$motif1}  =' "${var1#$motif1}"    #                             d12345abc6789
#  Correspondance la plus petite, supprime les trois premiers caractères de abcd12345abc6789
#                                                    ^^^^^^^^               |-|
echo '${var1##$motif1} =' "${var1##$motif1}"   #                                      6789      
#  Correspondance la plus grande possible, supprime les 12 premiers caractères de abcd12345abc6789
#                                                          ^^^^^^^^               |----------|

echo; echo; echo

motif2=b*9             # tout entre 'b' et '9'
echo "var1 = $var1"     # Toujours  abcd12345abc6789
echo

echo "motif2 = $motif2"
echo "------------------"
echo '${var1%motif2}  =' "${var1%$motif2}"     #                        abcd12345a
#  Correspondance la plus petite, supprime les six derniers caractères de abcd12345abc6789
#                                                  ^^^^^^^^                      |----|
echo '${var1%%motif2} =' "${var1%%$motif2}"    #                       a
#  Correspondance la plus grande, supprime les quinze derniers caractères de abcd12345abc6789
#                                                    ^^^^^^^^                |-------------|

#  Souvenez-vous, # et ## fonctionnent à partir de la gauche (début) de la chaîne
#                 % et %% fonctionnent à partir de la droite.

echo

exit 0

Exemple 10.11. Renommer des extensions de fichiers :

#!/bin/bash

# rfe.sh : Renommer des extensions de fichier (Renaming File Extensions).
#
#         rfe ancienne_extension nouvelle_extension
#
# Exemple :
# Pour renommer tous les fichiers *.gif d'un répertoire en *.jpg,
#          rfe gif jpg

E_MAUVAISARGS=65

case $# in
  0|1)             # La barre verticale signifie "ou" dans ce contexte.
  echo "Usage: `basename $0` ancien_suffixe nouveau_suffixe"
  exit $E_MAUVAISARGS  # Si 0 ou 1 argument, alors quitter.
  ;;
esac


for fichier in *.$1
# Traverse la liste des fichiers dont le nom termine avec le premier argument.
do
  mv $fichier ${fichier%$1}$2
  #  Supprime la partie du fichier contenant le premier argument
  #  puis ajoute le deuxième argument.
done

exit 0

Remplacement de variables / Remplacement de sous-chaînes

Ce type de syntaxe vient de ksh.

${var:pos}

La variable var remplacée par sa valeur, prise à partir de la position pos.

${var:pos:len}

Remplacement de la variable var par sa valeur, prise à partir de la position pos et sur au plus len caractères. Voir l'Exemple A.13, « password: Générer des mots de passe aléatoires de 8 caractères » pour un exemple d'utilisation particulièrement intéressant de cet opérateur.

${var/Motif/Remplacement}

Première occurrence de Motif, à l'intérieur de var remplacé par Remplacement.

Si Remplacement est omis, alors la première occurrence de Motif est remplacée par rien, c'est-à-dire supprimée.

${var//Motif/Remplacement}

Remplacement global. Toutes les occurrences de Motif, à l'intérieur de var sont remplacées par Remplacement.

Comme ci-dessus, si Remplacement est omis, alors toutes les occurrences de Motif sont remplacées par rien, c'est-à-dire supprimées.

Exemple 10.12. Utilisation de la concordance de motifs pour analyser diverses chaînes de caractères

#!/bin/bash

var1=abcd-1234-defg
echo "var1 = $var1"

t=${var1#*-*}
echo "var1 (on supprime tout ce qui se trouve jusqu'au premier -, y compris ce -) = $t"
#  t=${var1#*-}  fonctionne de la même façon,
#+ car # correspond à la plus petite chaîne de caractères,
#+ et * correspond à tout ce qui précède, même la chaîne vide.
# (Merci, Stéphane Chazelas, pour cette indication.)

t=${var1##*-*}
echo "Si var1 contient un \"-\", renvoie une chaîne vide...   var1 = $t"


t=${var1%*-*}
echo "var1 (on supprime tout à partir du dernier -) = $t"

echo

# -------------------------------------------
nom_chemin=/home/bozo/idees/pensees.pour.aujourdhui
# -------------------------------------------
echo "nom_chemin = $nom_chemin"
t=${nom_chemin##/*/}
echo "nom_chemin, sans les préfixes = $t"
# Même effet que   t=`basename $nom_chemin` dans ce cas particulier.
#  t=${nom_chemin%/}; t=${t##*/}   est une solution plus générale,
#+ mais elle échoue quelques fois.
#  Si $nom_chemin finit avec un retour chariot, alors `basename $nom_chemin`
#+ ne fonctionnera pas mais l'expression ci-dessus le fera.
# (Merci, S.C.)

t=${nom_chemin%/*.*}
# Même effet que t=`dirname $nom_chemin`
echo "nom_chemin, sans les suffixes = $t"
# Ceci va échouer dans certains cas, comme "../", "/foo////", # "foo/", "/".
#  Supprimer les suffixes, spécialement quand le nom de base n'en a pas, mais
#+ que le nom du répertoire en a un, complique aussi le problème.
# (Merci, S.C.)

echo

t=${nom_chemin:11}
echo "$nom_chemin, avec les 11 premiers caractères en moins = $t"
t=${nom_chemin:11:5}
echo "$nom_chemin, avec les 11 premiers caractères en moins et sur 5 caractères = $t"

echo

t=${nom_chemin/bozo/clown}
echo "$nom_chemin avec \"bozo\" remplacé par \"clown\" = $t"
t=${nom_chemin/aujourdhui/}
echo "$nom_chemin avec \"aujourdhui\" supprimé = $t"
t=${nom_chemin//o/O}
echo "$nom_chemin avec tous les o en majuscules = $t"
t=${nom_chemin//o/}
echo "$nom_chemin avec tous les o en moins = $t"

exit 0

${var/#Motif/Remplacement}

Si le préfixe de var est conforme à Motif, alors Motif est remplacé par Remplacement.

${var/%Motif/Remplacement}

Si le suffixe de var est conforme à Motif, alors Motif est remplacé par Remplacement.

Exemple 10.13. Motifs se conformant au préfixe ou au suffixe d'une chaîne de caractères

#!/bin/bash
# varmatch.sh
#  Démonstration de remplacement de motif sur le préfixe ou suffixe d'une chaîne de
#+ caractères.

v0=abc1234zip1234abc    # Variable de départ.
echo "v0 = $v0"         # abc1234zip1234abc
echo

# Correspond au préfixe (début) d'une chaîne de caractères.
v1=${v0/#abc/ABCDEF}    # abc1234zip1234abc
                        # |-|
echo "v1 = $v1"         # ABCDEF1234zip1234abc
                        # |----|

# Correspond au suffixe (fin) d'une chaîne de caractères.
v2=${v0/%abc/ABCDEF}    # abc1234zip1234abc
                        #               |-|
echo "v2 = $v2"         # abc1234zip1234ABCDEF
                        #               |----|

echo

#  ----------------------------------------------------
#  Doit correspondre au début / fin d'une chaîne de caractères.
#  sinon aucun remplacement ne se fera.
#  ----------------------------------------------------
v3=${v0/#123/000}       # Correspond, mais pas au début.
echo "v3 = $v3"         # abc1234zip1234abc
                        # PAS DE REMPLACEMENT.
v4=${v0/%123/000}       # Correspond, mais pas à la fin.
echo "v4 = $v4"         # abc1234zip1234abc
                        # PAS DE REMPLACEMENT.

exit 0                  

${!debutvar*}, ${!debutvar@}

Établit une correspondance des noms de toutes les variables déjà déclarées commençant par debutvar.

# Voici une variation sur la référence
#+ indirecte, mais avec un * ou un @.
# Cette fonctionnalité a été ajoutée dans la version 2.04 de Bash.

xyz23=quelquechose
xyz24=

a=${!xyz*}      # Se développe en les *noms* des variables déclarées précédemment
# ^ ^   ^       #+ et commençant par "xyz".
echo "a = $a"   # a = xyz23 xyz24
a=${!xyz@}      # Même chose que ci-dessus.
echo "a = $a"   # a = xyz23 xyz24

echo "---"

abc23=autrechose
b=${!abc*}
echo "b = $b"      #  b = abc23 
c=${!b}            #  à présent, une référence indirecte d'un genre plus familier
echo $c            #  autrechose


[50] Si $parametre est nul dans un script non interactif, il se terminera avec le code de retour 127 (code d'erreur de Bash pour « commande introuvable »).