28.2. /proc

Le répertoire /proc est en fait un pseudo système de fichiers. Les fichiers dans le répertoire /proc sont un miroir du système en cours d'exécution et des processus du noyau, et contiennent des informations et des statistiques sur elles.

bash$ cat /proc/devices
Character devices:
   1 mem
   2 pty
   3 ttyp
   4 ttyS
   5 cua
   7 vcs
  10 misc
  14 sound
  29 fb
  36 netlink
 128 ptm
 136 pts
 162 raw
 254 pcmcia

 Block devices:
   1 ramdisk
   2 fd
   3 ide0
   9 md



bash$ cat /proc/interrupts
           CPU0       
   0:      84505          XT-PIC  timer
   1:       3375          XT-PIC  keyboard
   2:          0          XT-PIC  cascade
   5:          1          XT-PIC  soundblaster
   8:          1          XT-PIC  rtc
  12:       4231          XT-PIC  PS/2 Mouse
  14:     109373          XT-PIC  ide0
 NMI:          0 
 ERR:          0



bash$ cat /proc/partitions
major minor  #blocks  name     rio rmerge rsect ruse wio wmerge wsect wuse running use aveq

    3     0    3007872 hda 4472 22260 114520 94240 3551 18703 50384 549710 0 111550 644030
    3     1      52416 hda1 27 395 844 960 4 2 14 180 0 800 1140
    3     2          1 hda2 0 0 0 0 0 0 0 0 0 0 0
    3     4     165280 hda4 10 0 20 210 0 0 0 0 0 210 210
    ...



bash$ cat /proc/loadavg
0.13 0.42 0.27 2/44 1119
         

Des scripts shells peuvent extraire des données à partir de certains des fichiers de /proc. [1]

bash$ cat /proc/filesystems | grep iso9660
        iso9660

	      

kernel_version=$( awk '{ print $3 }' /proc/version )

CPU=$( awk '/model name/ {print $4}' < /proc/cpuinfo )

if [ $CPU = Pentium ]
then
  lance_des_commandes
  ...
else
  lance_des_commandes_différentes
  ...
fi

Le répertoire /proc contient des sous-répertoires avec des noms numériques inhabituels. Chacun de ces noms correspond à un numéro de processus d'un processus en cours d'exécution. A l'intérieur de ces sous-répertoires, il existe un certain nombre de fichiers contenant des informations sur le processus correspondant. Les fichiers stat et status maintiennent des statistiques sur le processus, le fichier cmdline contient les arguments de la ligne de commande avec lesquels le processus a été appelé et le fichier exe est un lien symbolique vers le chemin complet du processus. Il existe encore quelques autres fichiers, mais ceux-ci sont les plus intéressants du point de vue de l'écriture de scripts.

Exemple 28-1. Trouver le processus associé à un PID

#!/bin/bash
# pid-identifier.sh: Donne le chemin complet du processus associé avec ce pid.

NBARGS=1  # Nombre d'arguments que le script attend.
E_MAUVAISARGS=65
E_MAUVAISPID=66
E_PROCESSUS_INEXISTANT=67
E_SANSDROIT=68
PROCFILE=exe

if [ $# -ne $NBARGS ]
then
  echo "Usage: `basename $0` PID" >&2  # Message d'erreur >stderr.
  exit $E_MAUVAISARGS
fi  

nopid=$( ps ax | grep $1 | awk '{ print $1 }' | grep $1 )
# Cherche pid dans l'affichage de "ps", car il est le champ #1.
# S'assure aussi qu'il s'agit du bon processus, et non pas du processus appelé
# par ce script.
# Le dernier "grep $1" supprime cette possibilité.
if [ -z "$nopid" ] # Si, après tous ces filtres, le résultat est une chaîne vide
then               # aucun processus en cours ne correspond au pid donné.
  echo "Aucun processus en cours."
  exit $E_PROCESSUS_INEXISTANT
fi  

# Autrement:
#   if ! ps $1 > /dev/null 2>&1
#   then                # Aucun processus ne correspond au pid donné.
#     echo "Ce processus n'existe pas"
#     exit $E_PROCESSUS_INEXISTANT
#    fi

# Pour simplifier tout cet algorithme, utilisez "pidof".


if [ ! -r "/proc/$1/$PROCFILE" ]  # Vérifiez les droits en lecture.
then
  echo "Processus $1 en cours, mais..."
  echo "Ne peut obtenir le droit de lecture sur /proc/$1/$PROCFILE."
  exit $E_SANSDROIT
    # Un utilisateur standard ne peut accéder à certains fichiers de /proc.
fi  

# Les deux derniers tests peuvent être remplacés par:
#    if ! kill -0 $1 > /dev/null 2>&1 # '0' n'est pas un signal, mais
                                      # ceci testera s'il est possible
                                      # d'envoyer un signal au processus.
#    then echo "PID n'existe pas ou vous n'êtes pas son propriétaire" >&2
#    exit $E_MAUVAISPID
#    fi



fichier_exe=$( ls -l /proc/$1 | grep "exe" | awk '{ print $11 }' )
# Ou       fichier_exe=$( ls -l /proc/$1/exe | awk '{print $11}' )
#
# /proc/pid-number/exe est un lien symbolique
# vers le chemin complet du processus appelé.

if [ -e "$fichier_exe" ]  # Si /proc/pid-number/exe existe...
then                   # le processus correspondant existe.
  echo "Processus #$1 appelé par $fichier_exe"
else
  echo "Processus inexistant"
fi  


# Ce script élaboré peut *pratiquement* être remplacé par
# ps ax | grep $1 | awk '{ print $5 }'
# Néanmoins, cela ne fonctionnera pas...
# parce que le cinquième champ de 'ps' est le argv[0] du processus,
# et non pas le chemin vers l'exécutable.
#
# Néanmoins, une des deux méthodes suivantes devrait fonctionner.
#       find /proc/$1/exe -printf '%l\n'
#       lsof -aFn -p $1 -d txt | sed -ne 's/^n//p'

# Commentaires supplémentaires par Stephane Chazelas.

exit 0

Exemple 28-2. Etat de la connexion

#!/bin/bash

NOMPROC=pppd           # démon ppp.
NOMFICHIERPROC=status  # Où chercher.
NONCONNECTE=65
INTERVALLE=2           # Mise à jour toutes les 2 secondes.

nopid=$( ps ax | grep -v "ps ax" | grep -v grep | grep $NOMPROC | awk '{ print $1 }' )
# Trouver le numéro de processus de 'pppd', le 'démon ppp'.
# Doit filtrer les lignes de processus générées par la recherche elle-même.
#
#  Néanmoins, comme Oleg Philon l'a indiqué,
#+ ceci pourrait être considérablement simplifié en utilisant "pidof".
#  nopid=$( pidof $NOMPROC )
#
#  Morale de l'histoire:
#+ Quand une séquence de commandes devient trop complexe, cherchez un raccourci.


if [ -z "$pidno" ]   # Si pas de pid, alors le processus ne tourne pas.
then
  echo "Non connecté."
  exit $NONCONNECTE
else
  echo "Connecté."; echo
fi

while [ true ]       # Boucle sans fin, le script peut être amélioré ici.
do

  if [ ! -e "/proc/$pidno/$NOMFICHIERPROC" ]
  #  Quand le processus est en cours d'exécution, alors le fichier "status"
  #+ existe.
  then
    echo "Disconnected."
    exit $NONCONNECTE
  fi

netstat -s | grep "packets received"  #  Obtenir quelques statistiques de
netstat -s | grep "packets delivered" #+ connexion.


  sleep $INTERVALLE
  echo; echo

done

exit 0

# De cette façon, le script ne se termine qu'avec un Control-C.

#    Exercices:
#    ---------
#    Améliorer le script pour qu'il se termine suite à l'appui sur la touche
#    "q".
#    Rendre le script plus facilement utilisable d'autres façons.

Avertissement

En général, il est dangereux d'écrire dans les fichiers de /proc, car cela peut corrompre le système de fichiers ou provoquer une erreur fatale.

Notes

[1]

Certaines commandes système, telles que procinfo, free, vmstat, lsdev, et uptime le font aussi.

ALIGN="LEFT" VALIGN="TOP" >

Exceptions:

Exemple 9-15. 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 "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}"

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#Modele}, ${var##Modele}

Supprimez à partir de $var la plus courte/longue partie de $Modele qui correspond au début de $var.

Un exemple d'usage à partir de Exemple A-8:
# Fonction provenant de l'exemple "days-between.sh"
# Supprimer les zéros du début à partir de l'argument donné.

supprimer_les_zeros_du_debut () # Il est mieux de supprimer les zéros éventuels
{                     # en début du  jour et/ou du mois
  val=${1#0}          # car sinon Bash les interprétera comme une valeur octale
  return $val         # (POSIX.2, sect 2.9.2.1).
}

Un autre exemple d'usage:
echo `basename $PWD`        # Base du nom du répertoire courant.
echo "${PWD##*/}"           # Base du nom du répertoire actuel.
echo
echo `basename $0`          # Nom du script.
echo $0                     # Nom du script.
echo "${0##*/}"             # Nom du script.
echo
filename=test.data
echo "${filename##*.}"      # données
                            # Extension du fichier.

${var%Modele}, ${var%%Modele}

Supprimez à partir de $var la partie la plus courte/longue de $Modele qui correspond à la fin de $var.

La version 2 de Bash ajoute des options supplémentaires.

Exemple 9-16. Correspondance de modèle dans la substitution de paramètres

#!/bin/bash
# Reconnaissance de modèles en utilisant les opérateurs de substituion # ## % %%

var1=abcd12345abc6789
modele1=a*c  # * (wild card) matches everything between a - c.

echo
echo "var1 = $var1"           # abcd12345abc6789
echo "var1 = ${var1}"         # abcd12345abc6789   (autre forme)
echo "Nombre de caractères dans ${var1} = ${#var1}"
echo "modele1 = $modele1"     # a*c  (tout entre 'a' et 'c')
echo


echo '${var1#$modele1}  =' "${var1#$modele1}"    #                         d12345abc6789
# Correspondance la plus petite, supprime les trois permiers caractères abcd12345abc6789
#                                     ^^^^^               |-|
echo '${var1##$modele1} =' "${var1##$modele1}"   #                                        6789      
# Correspondance la plus grande possible, supprime les 12 premiers caractères abcd12345abc6789
#                                    ^^^^^                |----------|

echo; echo

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

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

#  Souvenez-vous, # et ## fonctionnent à partir de la gauche de la fin de la
#+ chaîne
#  % and %% fonctionnent à partir de la droite.

echo

exit 0

Exemple 9-17. Renommer des extensions de fichiers:

#!/bin/bash

#                 rfe
#                 ---

# Renommer les 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

ARGS=2
E_MAUVAISARGS=65

if [ $# -ne "$ARGS" ]
then
  echo "Usage: `basename $0` ancien_suffixe nouveau_suffixe"
  exit $E_MAUVAISARGS
fi

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

Expansion de variables / Remplacement de sous-chaînes

Ces constructions proviennent de ksh.

${var:pos}

La variable var étendue, commençant à la position pos.

${var:pos:len}

Augmentation d'un maximum de len caractères de la variable var, à partir de la position pos. Voir Exemple A-15 pour un exemple d'utilisation particulièrement intéressante de cet opérateur.

${var/Modele/Remplacement}

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

Si Remplacement est omis, alors la première occurrence de Modele n'est remplacé par rien, c'est-à-dire qu'il est supprimé.

${var//Modele/Remplacement}

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

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

Exemple 9-18. Utiliser la recherche de modèles pour analyser des chaînes de caractères diverses

#!/bin/bash

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

t=${var1#*-*}
echo "var1 (avec tout, jusqu'au et incluant le premier - supprimé) = $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, incluant la chaîne vide.
# (Merci, S. C. pour l'avoir indiqué.)

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


t=${var1%*-*}
echo "var1 (avec tout à partir de la fin - supprimée) = $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 lpus 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 supprimés = $t"
t=${nom_chemin:11:5}
echo "$nom_chemin, avec les 11 premiers caractères supprimés, longueur 5 = $t"

echo

t=${nom_chemin/bozo/clown}
echo "$nom_chemin avec \"bozo\" remplacé par \"clown\" = $t"
t=${nom_chemin/today/}
echo "$nom_chemin avec \"today\" supprimé = $t"
t=${nom_chemin//o/O}
echo "$nom_chemin avec tous les o en majuscule = $t"
t=${nom_chemin//o/}
echo "$nom_chemin avec tous les o supprimés = $t"

exit 0
${var/#Modele/Remplacement}

Si le préfixe de var correspond à Modele, alors Remplacement remplace Modele.

${var/%Modele/Remplacement}

Si le suffixe de var correspond à Modele, alors Remplacement remplace Modele.

Exemple 9-19. Modèles correspondant au préfixe ou au suffixe d'une chaîne de caractères

#!/bin/bash
# Remplacement de modèle sur le préfixe / suffixe d'une chaîne de caractères.

v0=abc1234zip1234abc    # Variable original.
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"         # ABCDE1234zip1234abc
                        # |---|

# Correspond au suffixe (fin) d'une chaîne de caractères.
v2=${v0/%abc/ABCDEF}    # abc1234zip123abc
                        #              |-|
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			
${!varprefixe*}, ${!varprefixe@}

Correspond à toutes les variables déjà déclarées commençant par varprefixe.
xyz23=quoiquecesoit
xyz24=

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

# Bash, version 2.04, ajoute cette fonctionnalité.

Notes

[1]

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

VALIGN="top" >PrécédentSommaireSuivantBoucles et branchementsNiveau supérieurBoucles imbriquées >

$SECONDS

Le nombre de secondes pendant lequel le script s'exécutait.

#!/bin/bash

LIMITE_TEMPS=10
INTERVALLE=1

echo
echo "Tapez sur Control-C pour sortir avant $LIMITE_TEMPS secondes."
echo

while [ "$SECONDES" -le "$LIMITE_TEMPS" ]
do
  if [ "$SECONDES" -eq 1 ]
  then
    unites=seconde
  else  
    unites=secondes
  fi

  echo "Ce script tourne depuis $SECONDES $unites."
  #  Sur une machine lente, le script peut laisser échapper un élément du
  #+ comptage quelque fois dans la boucle while.
  sleep $INTERVALLE
done

echo -e "\a"  # Beep!

exit 0

$SHELLOPTS

la liste des options activées du shell, une variable en lecture seule
bash$ echo $SHELLOPTS
braceexpand:hashall:histexpand:monitor:history:interactive-comments:emacs
	      

$SHLVL

Niveau du shell, comment Bash est imbriqué. Si, à la ligne de commande, $SHLVL vaut 1, alors, dans un script, il sera incrémenté et prendra la valeur 2.

$TMOUT

Si la variable d'environnement $TMOUT est initialisée à une valeur différente de zéro appelée time, alors l'invite shell dépassera son délai au bout de time secondes. Ceci causera une déconnexion.

Note

Malheureusement, ceci fonctionne seulement lors de l'attente d'une saisie sur une invite de la console ou dans un xterm. Bien qu'il serait sympathique de spéculer sur l'utilité de cette variable interne pour des saisies avec expiration de délai, par exemple en combinaison avec read, $TMOUT ne fonctionnera pas dans ce contexte et est virtuellement inutile pour l'écriture de scripts shell. (Une information semble indiquer qu'un read avec délai fontionne sur ksh.)

Implémenter une saisie avec délai dans un script est certainement possible, mais nécessiterait un code complexe. Une méthode est de configurer une boucle avec délai pour signaler au script lorsque le délai se termine. Ceci nécessite aussi une routine de gestion du signal pour récupérer (voir Exemple 30-5) l'interruption générée par la boucle de délai (ouf!).

Exemple 9-2. Saisie avec délai

#!/bin/bash
# timed-input.sh

# TMOUT=3            inutile dans un script

LIMITETEMPS=3  # Trois secondes dans cette instance, peut être configuré avec
               #+ une valeur différente.

AfficheReponse()
{
  if [ "$reponse" = TIMEOUT ]
  then
    echo $reponse
  else       # ne pas mixer les deux interfaces.
    echo "Votre légume favori est le $reponse"
    kill $!  #  Kill n'est plus nécessaire pour la fonction TimerOn lancé en
             #+ tâche de fond.
             # $! est le PID du dernier job lancé en tâche de fond.
  fi

}  



TimerOn()
{
  sleep $LIMITETEMPS && kill -s 14 $$ &
  # Attend 3 secondes, puis envoie sigalarm au script.
}  

VecteurInt14()
{
  reponse="TIMEOUT"
  AfficheReponse
  exit 14
}  

trap VecteurInt14 14   # Interruption de temps (14) détournée pour notre but.

echo "Quel est votre légume favori?"
TimerOn
read reponse
AfficheReponse


#  C'est une implémentation détournée de l'entrée de temps,
#+ néanmoins l'option "-t" de "read" simplifie cette tâche.
#  Voir "t-out.sh", ci-dessous.

#  Si vous avez besoin de quelque chose de réellement élégant...
#+ pensez à écrire l'application en C ou C++,
#+ en utilisant les fonctions de la bibliothèque appropriée, telles que
#+ 'alarm' et 'setitimer'.

exit 0

Une autre méthode est d'utiliser stty.

Exemple 9-3. Encore une fois, saisie avec délai

#!/bin/bash
# timeout.sh

# Ecrit par Stephane Chazelas,
# et modifié par l'auteur de ce document.

INTERVALLE=5                # timeout interval

lecture_timedout() {
  timeout=$1
  nomvariable=$2
  ancienne_configuration_tty=`stty -g`
  stty -icanon min 0 time ${timeout}0
  eval read $nomvariable      # ou simplement    read $nomvariable
  stty "$ancienne_configuration_tty"
  # Voir la page man de "stty".
}

echo; echo -n "Quel est votre nom? Vite!"
lecture_timedout $INTERVALLE votre_nom

# Ceci pourrait ne pas fonctionner sur tous les types de terminaux.
# Le temps imparti dépend du terminal (il est souvent de 25,5 secondes).

echo

if [ ! -z "$votre_nom" ]  #  Si le nom est entré avant que le temps ne se soit
                          #+ écoulé...
then
  echo "Votre nom est $votre_nom."
else
  echo "Temps écoulé."
fi

echo

# Le comportement de ce script diffère un peu de "timed-input.sh".
# A chaque appui sur une touche, le compteur est réinitialisé.

exit 0

Peut-être que la méthode la plus simple est d'utiliser l'option -t de read.

Exemple 9-4. read avec délai

#!/bin/bash
# t-out.sh (suggestion de "syngin seven")

LIMITETEMPS=4        # 4 secondes

read -t $LIMITETEMPS variable <&1

echo

if [ -z "$variable" ]
then
  echo "Temps écoulé, la variable n'est toujours pas initialisée."
else  
  echo "variable = $variable"
fi  

exit 0
$UID

numéro de l'identifiant utilisateur

numéro d'identification de l'utilisateur actuel, comme enregistré dans /etc/passwd

C'est l'identifiant réel de l'utilisateur actuel, même s'il a temporairement endossé une autre identité avec su. $UID est une variable en lecture seule, non sujet au changement à partir de la ligne de commande ou à l'intérieur d'un script, et est la contre partie de l'intégré id.

Exemple 9-5. Suis-je root?

#!/bin/bash
# am-i-root.sh:   Suis-je root ou non?

ROOT_UID=0   # Root a l'identifiant $UID 0.

if [ "$UID" -eq "$ROOT_UID" ]  # Le vrai "root" peut-il se lever, s'il-vous-plaît?
then
  echo "Vous êtes root."
else
  echo "Vous êtes simplement un utilisateur ordinaire (mais maman vous aime tout autant.)."
fi

exit 0


# ============================================================= #
# Le code ci-dessous ne s'exécutera pas, parce que le script s'est déjà arrêté.

# Une autre méthode d'arriver à la même fin:

NOM_UTILISATEURROOT=root

nomutilisateur=`id -nu`              # Ou...   nomutilisateur=`whoami`
if [ "$nomutilisateur" = "$NOM_UTILISATEURROOT" ]
then
  echo "Vous êtes root."
else
  echo "Vous êtes juste un gars régulier."
fi

Voir aussi Exemple 2-2.

Note

Les variables $ENV, $LOGNAME, $MAIL, $TERM, $USER et $USERNAME ne sont pas des variables intégrés à Bash. Elles sont néanmois souvent initialisées comme variables d'environnement dans un des fichiers de démarrage de Bash. $SHELL, le nom du shell de connexion de l'utilisateur, peut être configuré à partir de /etc/passwd ou dans un script d'<< initialisation >>, et ce n'est pas une variable intégrée à Bash.

tcsh% echo $LOGNAME
bozo
tcsh% echo $SHELL
/bin/tcsh
tcsh% echo $TERM
rxvt

bash$ echo $LOGNAME
bozo
bash$ echo $SHELL
/bin/tcsh
bash$ echo $TERM
rxvt
	      

Paramètres de position

$0, $1, $2, etc.

paramètres de positions, passés à partir de la ligne de commande à un script, passés à une fonction, ou initialisés (set) à une variable (voir Exemple 4-5 et Exemple 11-13)

$#

nombre d'arguments sur la ligne de commande [2] ou de paramètres de position (voir Exemple 34-2)

$*

Tous les paramètres de position, vus comme un seul mot

$@

Identique à $*, mais chaque paramètre est une chaîne entre guillemets, c'est-à-dire que les paramètres sont passés de manière intacte, sans interprétation ou expansion. Ceci signifie, entre autres choses, que chaque paramètre dans la liste d'arguments est vu comme un mot séparé.

Exemple 9-6. arglist: Affichage des arguments avec $* et $@

#!/bin/bash
# Appelez ce script avec plusieurs arguments, tels que "un deux trois".

E_BADARGS=65

if [ ! -n "$1" ]
then
  echo "Usage: `basename $0` argument1 argument2 etc."
  exit $E_BADARGS
fi  

echo

index=1

echo "Liste des arguments avec \"\$*\":"
for arg in "$*"  # Ne fonctionne pas correctement si "$*" n'est pas entre guillemets.
do
  echo "Arg #$index = $arg"
  let "index+=1"
done             # $* voit tous les arguments comme un mot entier. 
echo "Liste entière des arguments vue comme un seul mot."

echo

index=1

echo "Liste des arguments avec \"\$@\":"
for arg in "$@"
do
  echo "Arg #$index = $arg"
  let "index+=1"
done             # $@ voit les arguments comme des mots séparés. 
echo "Liste des arguments vue comme des mots séparés."

echo

exit 0

Suite à un shift, $@ contient le reste des paramètres de la ligne de commande, sans le précédent $1, qui a été perdu.
#!/bin/bash
# Appelé avec ./script 1 2 3 4 5

echo "$@"    # 1 2 3 4 5
shift
echo "$@"    # 2 3 4 5
shift
echo "$@"    # 3 4 5

# Chaque "shift" perd le paramètre $1.
# "$@" contient alors le reste des paramètres.

Le paramètre spécial $@ trouve son utilité comme outil pour filtrer l'entrée des scripts shell. La construction cat "$@" accepte l'entrée dans un script soit à partir de stdin soit à partir de fichiers donnés en paramètre du script. Voir Exemple 12-17 et Exemple 12-18.

Attention

Les paramètres $* et $@ affichent quelque fois un comportement inconsistent et bizarre, suivant la configuration de $IFS.

Exemple 9-7. Comportement de $* et $@ inconsistent

#!/bin/bash

#  Comportement non prédictible des variables internes Bash "$*" et "$@",
#+ suivant qu'elles soient ou non entre guillemets.
#  Gestion inconsistente de la séparation de mots et des retours chariot.


set -- "Premier un" "second" "troisième:un" "" "Cinquième: :un"
# Initialise les arguments du script, $1, $2, etc.

echo

echo 'IFS inchangée, utilisant "$*"'
c=0
for i in "$*"               # entre guillemets
do echo "$((c+=1)): [$i]"   # Cette ligne reste identique à chaque instance.
                            # Arguments de echo.
done
echo ---

echo 'IFS inchangée, utilisant $*'
c=0
for i in $*                 # entre guillemets
do echo "$((c+=1)): [$i]"
done
echo ---

echo 'IFS inchangée, utilisant "$@"'
c=0
for i in "$@"
do echo "$((c+=1)): [$i]"
done
echo ---

echo 'IFS inchangée, utilisant $@'
c=0
for i in $@
do echo "$((c+=1)): [$i]"
done
echo ---

IFS=:
echo 'IFS=":", utilisant "$*"'
c=0
for i in "$*"
do echo "$((c+=1)): [$i]"
done
echo ---

echo 'IFS=":", utilisant $*'
c=0
for i in $*
do echo "$((c+=1)): [$i]"
done
echo ---

var=$*
echo 'IFS=":", utilisant "$var" (var=$*)'
c=0
for i in "$var"
do echo "$((c+=1)): [$i]"
done
echo ---

echo 'IFS=":", utilisant $var (var=$*)'
c=0
for i in $var
do echo "$((c+=1)): [$i]"
done
echo ---

var="$*"
echo 'IFS=":", utilisant $var (var="$*")'
c=0
for i in $var
do echo "$((c+=1)): [$i]"
done
echo ---

echo 'IFS=":", utilisant "$var" (var="$*")'
c=0
for i in "$var"
do echo "$((c+=1)): [$i]"
done
echo ---

echo 'IFS=":", utilisant "$@"'
c=0
for i in "$@"
do echo "$((c+=1)): [$i]"
done
echo ---

echo 'IFS=":", utilisant $@'
c=0
for i in $@
do echo "$((c+=1)): [$i]"
done
echo ---

var=$@
echo 'IFS=":", utilisant $var (var=$@)'
c=0
for i in $var
do echo "$((c+=1)): [$i]"
done
echo ---

echo 'IFS=":", utilisant "$var" (var=$@)'
c=0
for i in "$var"
do echo "$((c+=1)): [$i]"
done
echo ---

var="$@"
echo 'IFS=":", utilisant "$var" (var="$@")'
c=0
for i in "$var"
do echo "$((c+=1)): [$i]"
done
echo ---

echo 'IFS=":", utilisant $var (var="$@")'
c=0
for i in $var
do echo "$((c+=1)): [$i]"
done

echo

# Essayez ce script avec ksh ou zsh -y.

exit 0

# Ce script exemple par Stephane Chazelas,
# et légèrement modifié par l'auteur de ce document.

Note

Les paramètres $@ et $* diffèrent seulement lorsqu'ils sont entre des guillemets doubles.

Exemple 9-8. $* et $@ lorsque $IFS est vide

#!/bin/bash

# Si $IFS est initialisé, mais vide,
# alors "$*" et "$@" n'affiche pas les paramètres de position comme on pourrait
# s'y attendre.

mecho ()       # Affiche les paramètres de position.
{
echo "$1,$2,$3";
}


IFS=""         # Initialisé, mais vide.
set a b c      # Paramètres de position.

mecho "$*"     # abc,,
mecho $*       # a,b,c

mecho $@       # a,b,c
mecho "$@"     # a,b,c

# Le comportement de $* et $@ quand $IFS est vide dépend de la version de
# Bash ou sh.
# Personne ne peux donc conseiller d'utiliser cette "fonctionnalité" dans un
# script.


# Merci, S.C.

exit 0

Autres paramètres spéciaux

$-

Les options passées au script (en utilisant set). Voir Exemple 11-13.

Attention

Ceci était originellement une construction de ksh adoptée dans Bash, et malheureusement elle ne semble pas fonctionner de façon fiable dans les scripts Bash. Une utilité possible pour ceci est d'avoir un script testant lui-même s'il est interactif.

$!

PID (identifiant du processus) du dernier job ayant fonctionné en tâche de fond

$_

Variable spéciale initialisée au dernier argument de la dernièr commande exécutée.

Exemple 9-9. variable tiret bas

#!/bin/bash

echo $_              # /bin/bash
                     # Simple appel de /bin/bash pour lancer ce script.

du >/dev/null        # Donc pas de sortie des commandes
echo $_              # du

ls -al >/dev/null    # Donc pas de sortie des commandes
echo $_              # -al  (dernier argument)

:
echo $_              # :
$?

Code de sortie d'une commande, fonction, ou du script lui-même (voir Exemple 23-3)

$$

Identifiant du processus du script lui-même. La variable $$ trouve fréquemment son utilité dans les scripts pour construire des noms de fichiers temporaires << uniques >> (voir Exemple A-14, Exemple 30-6, Exemple 12-23 et Exemple 11-23). Ceci est généralement plus simple que d'appeler mktemp.

Notes

[1]

Le pid du script en cours est $$, bien sûr.

[2]

Les mots << argument >> et << paramètre >> sont souvent utilisés sans distinction. Dans le contexte de ce document, ils ont exactement la même signification, celle d'une variable passée à un script ou à une fonction.

>

#!/bin/bash

exec echo "Je sors \"$0\"."   # Sortie du script ici.

# ----------------------------------
# Les lignes suivantes ne s'exécutent jamais.

echo "Cet echo ne sera jamais exécuté."

exit 99                       #  Ce script ne sortira jamais par ici.
                              #  Vérifier le code de sortie après l'exécution du
                              #+ du script avec un 'echo $?'.
                              #  Cela ne sera *pas* 99.

Exemple 11-21. Un script lançant exec sur lui-même

#!/bin/bash
# self-exec.sh

echo

echo "Cette ligne apparaît UNE FOIS dans le script, cependant elle continue à s'afficher."
echo "Le PID de cette instance du script est toujours $$."
#     Démontre qu'un sous-shell n'est pas un processus fils.

echo "==================== Tapez Ctl-C pour sortir ===================="

sleep 1

exec $0   #  Lance une autre instance du même script remplaçant le précédent.

echo "Cette ligne ne s'affichera jamais!"  # Pourquoi pas?

exit 0

Un exec sert aussi à réaffecter les descripteurs de fichiers.exec <fichier-zzz remplace stdin par le fichier fichier-zzz (voir Exemple 16-1).

Note

L'option -exec pour find n'est pas du tout la même chose que la commande shell intégrée exec.

shopt

Cette commande permet de changer les options du shell au vol (voir Exemple 24-1 et Exemple 24-2). Elle apparait souvent dans les fichiers de démarrage de Bash, mais a aussi son utilité dans des scripts. Il est nécessaire de disposer de la version 2, ou ultérieurs, de Bash.
shopt -s cdspell
# Permet des petites erreurs dans le nom des répertoires avec 'cd'

cd /hpme  # Oups! J'ai mal tapé '/home'.
pwd       # /home
          # Le shell a corrigé la faute de frappe.

Commandes

true

Une commande qui renvoie un succès (zéro) comme état de sortie, mais ne fait rien d'autre.

# Boucle sans fin
while true   # alias pour ":"
do
   operation-1
   operation-2
   ...
   operation-n
   # A besoin d'un moyen pour sortir de la boucle.
done

false

Une commande qui renvoit un état de sortie correspondant à un échec, mais ne fait rien d'autre.

# Fausse boucle
while false
do
   # Le code suivant ne sera pas exécuté.
   operation-1
   operation-2
   ...
   operation-n
   # Rien ne se passe!
done   

type [cmd]

Identique à la commande externe which, type cmd donne le chemin complet vers << cmd >>. Contrairement à which, type est une commande intégrée à Bash. L'option -a est très utile pour que type identifie des mots clés et des commandes internes, et localise aussi les commandes système de nom identiques.

bash$ type '['
[ is a shell builtin
bash$ type -a '['
[ is a shell builtin
 [ is /usr/bin/[
	      

hash [cmds]

Enregistre le chemin des commandes spécifiées (dans une table de hachage du shell), donc le shell ou le script n'aura pas besoin de chercher le $PATH sur les appels futurs à ces commandes. Quand hash est appelé sans arguments, il liste simplement les commandes qui ont été hachées. L'option -r réinitialise la table de hachage.

help

help COMMANDE cherche un petit résumé sur l'utilisation de la commande COMMANDE intégrée au shell. C'est l'équivalent de whatis, pour les commandes intégrées.

bash$ help exit
exit: exit [n]
    Exit the shell with a status of N.  If N is omitted, the exit status
    is that of the last command executed.
	      

Notes

[1]

Une exception à ceci est la commande time, listée dans la documentation Bash officielle en tant que mot clé.

[2]

Une option est un argument agissant comme un indicateur, changeant les comportements du script de façon binaire. L'argument associé avec une option particulière indique le comportement que l'option active ou désactive.