4.4. Types de variables spéciales

variables locales

variables visibles uniquement à l'intérieur d'un bloc de code ou d'une fonction (voir aussi variables locales dans le chapitre sur les fonctions)

variables d'environnement

variables qui affectent le comportement du shell et de l'interface utilisateur

[Note]

Note

Dans un contexte plus général, chaque processus a un « environnement », c'est-à-dire un groupe de variables contenant des informations auxquelles pourrait faire référence le processus. En ce sens, le shell se comporte comme n'importe quel processus.

Chaque fois qu'un shell démarre, il crée les variables shell correspondantes à ses propres variables d'environnement. Mettre à jour ou ajouter de nouvelles variables d'environnement force le shell à mettre à jour son environnement, et tous les processus fils (les commandes qu'il exécute) héritent de cet environnement.

[Attention]

Attention

L'espace alloué à l'environnement est limité. Créer trop de variables d'environnement ou une variable d'environnement qui utilise trop d'espace peut provoquer des ennuis.

bash$ eval "`seq 10000 | sed -e 's/.*/export var&=ZZZZZZZZZZZZZZ/'`"

bash$ du
bash: /usr/bin/du: Argument list too long
                  

(Merci à Stéphane Chazelas pour la clarification et pour avoir fourni l'exemple ci-dessus.)

Quand un script déclare des variables d'environnement, il faut ensuite les « exporter », c'est-à-dire les ajouter à l'environnement local du script. C'est le rôle de la commande export.

[Note]

Note

Un script peut exporter des variables seulement vers ses processus fils, c'est-à-dire seulement vers les commandes ou processus que ce script en particulier initie. Un script invoqué depuis la ligne de commande ne peut pas ré-exporter des variables à destination de l'environnement de la ligne de commande dont il est issu. Les processus enfants ne peuvent pas réexporter de variables vers les processus parents qui les ont fait naître.

Définition : un processus fils est un sous-processus lancé par un autre processus qui est alors son parent.

paramètres de position

Ce sont les arguments passés aux scripts depuis la ligne de commande [25] : $0, $1, $2, $3...

$0 est le nom du script lui-même, $1 est le premier argument, $2 le second, $3 le troisième, et ainsi de suite. [26] Après $9, les arguments doivent être entourés par des accolades, comme dans ${10}, ${11}, ${12}.

Les variables spéciales $* et $@ représentent tous les paramètres de position.

Exemple 4.5. Paramètres de position

#!/bin/bash

# Appelez ce script avec au moins 10 paramètres, par exemple
# ./nom_du_script 1 2 3 4 5 6 7 8 9 10
MINPARAMS=10

echo

echo "Le nom de ce script est \"$0\"."
# Ajoute ./ pour le répertoire courant.
echo "Le nom de ce script est \"`basename $0`\"."
# Supprime le chemin du script (voir 'basename')

echo

if [ -n "$1" ]                   # La variable testée est entre guillemets.
then
 echo "Le paramètre n°1 est $1"  # Nous avons besoin des guillemets pour échapper #
fi 

if [ -n "$2" ]
then
 echo "Le paramètre n°2 est $2"
fi 

if [ -n "$3" ]
then
 echo "Le paramètre n°3 est $3"
fi 

# ...


if [ -n "${10}" ]  #  Les paramètres supérieures à $9 doivent être compris entre
                   #+ accolades.
then
 echo "Le paramètre n°10 est ${10}"
fi 

echo "-----------------------------------"
echo "Tous les paramètres de la ligne de commande : "$*""

if [ $# -lt "$MINPARAMS" ]
then
  echo
  echo "Ce script a besoin d'au moins $MINPARAMS arguments en ligne de commande!"
fi  

echo

exit 0

La notation entre accolades pour les paramètres de position permet de référencer assez simplement le dernier argument passé à un script sur la ligne de commande. Pour cela on utilise également le référencement indirect.

args=$#           # Nombre d'arguments passés.
dernarg=${!args}
# Note: c'est une *référence indirecte* à $args ...

# Ou :      dernierarg=${!#}           (Merci à Chris Monson)
# C'est une *référence indirecte* à la variable $#.
# Remarque : dernierarg=${!$#} ne fonctionne pas.

Certains scripts peuvent effectuer diverses opérations suivant le nom sous lequel ils ont été invoqués. Pour cela, le script doit tester $0, le nom sous lequel il a été invoqué. [27] Il doit y avoir aussi des liens symboliques vers tous les noms alternatifs du script. Voir l'Exemple 16.2, « Hello or Good-bye ».

[Astuce]

Astuce

Si un script attend un paramètre en ligne de commande mais qu'il est invoqué sans, cela peut causer une affectation à valeur vide, généralement un résultat non désiré. Une façon de l'empêcher est l'ajout d'un caractère supplémentaire des deux côtés de l'instruction d'affectation utilisant le paramètre positionnel attendu.

variable1_=$1_ # Plutôt que
variable1_=$1
# Cela empêchera l'erreur, même si le paramètre positionnel est absent.
            
argument_critique01=$variable1_

# Le caractère supplémentaire peut être retiré plus tard comme ceci.
variable1=${variable1_/_/}
# Il n'y aura d'effets de bord que si $variable1_ commence par un tiret bas.
# Ceci utilise un des patrons de substitution de paramètres discutés plus tard
# (laisser vide le motif de remplacement aboutit à une suppression).

# Une façon plus directe de résoudre ce problème est de simplement tester
#+ si le paramètre postionnel attendu a bien été passé.
if [ -z $1 ]
then
  exit $E_PARAM_POS_MANQUANT
fi


#  Néanmoins, comme l'indique Fabian Kreutz,
#+ la méthode ci-dessus pourrait avoir des effets de bord.
#  Une meilleure méthode est la substitution de paramètres :
#         ${1:-$DefaultVal}
#  Voir la section « Substitution de paramètres »
#+ dans le chapitre « Les variables revisitées ».

---

Exemple 4.6. wh, recherche d'un nom de domaine avec whois

#!/bin/bash
# ex18.sh

# Fait une recherche 'whois nom-domaine' sur l'un des trois serveurs:
#                    ripe.net, cw.net, radb.net

# Placez ce script -- renommé 'wh' -- dans /usr/local/bin

# Requiert les liens symboliques :
# ln -s /usr/local/bin/wh /usr/local/bin/wh-ripe
# ln -s /usr/local/bin/wh /usr/local/bin/wh-cw
# ln -s /usr/local/bin/wh /usr/local/bin/wh-radb

E_SANSARGS=65

if [ -z "$1" ]
then
  echo "Usage: `basename $0` [nom-domaine]"
  exit $E_SANSARGS
fi

# Vérifie le nom du script et appelle le bon serveur
case `basename $0` in # Ou :    case ${0##*/} in
    "wh"     ) whois $1@whois.ripe.net;;
    "wh-ripe") whois $1@whois.ripe.net;;
    "wh-radb") whois $1@whois.radb.net;;
    "wh-cw"  ) whois $1@whois.cw.net;;
    *        ) echo "Usage: `basename $0` [nom-domaine]";;
esac 

exit $?

---

La commande shift réaffecte les paramètres de position, ce qui produit le même effet que de les déplacer d'un cran vers la gauche.

$1 <--- $2, $2 <--- $3, $3 <--- $4, etc.

Le vieux $1 disparaît mais $0 (le nom du script) ne change pas. Si vous faites usage d'un grand nombre de paramètres positionnels dans un script, shift vous permet d'accèder à ceux au-delà de 10, bien que la notation {entre accolades} le permette également.

Exemple 4.7. Utiliser shift

#!/bin/bash
# shft.sh : Utilisation de 'shift' pour voir tous les paramètres de position.

#  Nommez ce script quelque chose comme shft.sh,
#+ et exécutez-le avec quelques paramètres.
#+ Par exemple :
#          sh shft.sh a b c def 23 skidoo

until [ -z "$1" ]  # Jusqu'à ce qu'il n'y ait plus de paramètres...
do
  echo -n "$1 "
  shift
done

echo               # Retour chariot supplémentaire.

exit 0

#  Voir aussi le script echo-params.sh
#+ pour une méthode n'utilisant pas shift 
#+ pour passer de paramètre en paramètre.

La commande shift peut prendre un paramètre numérique indiquant le nombre de décalages.

#!/bin/bash
# shift-past.sh

shift 3    # Décale de 3 cases.
#  n=3; shift $n
#  produit le même effet.

echo "$1"

exit 0


$ sh shift-past.sh 1 2 3 4 5
4

#  Néanmoins, comme Eleni Fragkiadaki l'indique,
#+ tenter un 'shift' après le nombre de paramètres de position
#+ ($#) renvoie 1 comme état de sortie, et les paramètres de
#+ position ne changent pas.
#  Cela signifie qu'on risque d'aboutir à une boucle sans fin...
#  Par exemple :
#      until [ -z "$1" ]
#      do
#         echo -n "$1 "
#         shift 20    #  Si moins de 20 paramètres de position,
#      done           #+ la boucle ne termine jamais !
#
# En cas de doute, ajoutez une vérification...
#           shift 20 || break
#                    ^^^^^^^^
[Note]

Note

La commande shift fonctionne d'une manière proche du passage de paramètres à une fonction. Voir l'Exemple 36.16, « Astuce de valeur de retour ».



[26] Le processus qui appelle le script représente la valeur du paramètre $0. Par convention, ce paramètre est le nom du script. Voir la page de manuel d'execv.

Cependant, en ligne de commande, $0 est le nom du shell.

bash$ echo $0
bash
            
tcsh% echo $0
tcsh

[27] Si le script est importé ou lié symboliquement, cela ne va pas fonctionner. Pour plus de sûreté, vérifier $BASH_Source.