3. Les caractères spéciaux

Qu'est-ce qui rend un caractère spécial ? S'il a une signification en dehors de la signification littérale, une meta signification, alors nous lui donnons le nom de caractère spécial.

Caractères spéciaux se trouvant dans les scripts et ailleurs

#

Commentaires. Les lignes commençant avec un # (à l'exception de #!) sont des commentaires et ne seront pas exécutées.

# Cette ligne est un commentaire.

Les commentaires peuvent apparaître après la fin d'une commande.

echo "Un commentaire va suivre." # Un commentaire ici.
#                               ^
# Notez l'espace avant le #

Les commentaires peuvent aussi être précédés d'un blanc en début de ligne.

        # Une tabulation précède ce commentaire.

On peut même inclure les commentaires à l'intérieur d'un tube.

initial=( `cat "$startfile" | sed -e '/#/d' | tr -d '\n' |\
# Supprimer les caractères '#' de commentaire.
           sed -e 's/\./\. /g' -e 's/_/_ /g'` )
# Extrait du script life.sh
[Attention]

Attention

Un commentaire ne peut pas être suivi d'une commande sur la même ligne. Il n'y a pas moyen de mettre fin au commentaire pour que le « vrai code » commence sur la même ligne. Utilisez une nouvelle ligne pour la commande suivante.

[Note]

Note

Bien sûr, un # entre guillemets ou échappé dans une instruction echo ne commence pas un commentaire. De la même manière, un # apparaît dans certaines expressions avec substitution de paramètres et dans les expressions numériques constantes.

echo "Le # ici ne commence pas un commentaire."
echo 'Le # ici ne commence pas un commentaire.'
echo Le \# ici ne commence pas un commentaire.
echo Le # ici commence un commentaire.

echo ${PATH#*:}       # Une substitution de paramètres, non pas un commentaire.
echo $(( 2#101011 ))  # Une conversion de base, non pas un commentaire.

# Merci à S.C.

Les caractères standard de guillemets et d'échappement (" ' \) échappent le #.

Certaines opérations de filtrage de motif font aussi appel au #.

;

Séparateur de commande [point-virgule]. Permet de placer deux commandes ou plus sur la même ligne.

echo bonjour; echo les gens

if [ -x "$nomfichier" ]; then  # Notez l'espace après le point-virgule
#+                     ^^      
  echo "Le fichier $nomfichier existe."; cp $nomfichier $nomfichier.sauve
else                 #                 ^^
  echo "Le fichier $nomfichier est introuvable."; touch $nomfichier
fi; echo "Test du fichier terminé."

Notez que le « ; » a parfois besoin d'être échappé.

;;

Fin de ligne dans une sélection par cas case [double point-virgule]. 

case "$variable" in
  abc)  echo "\$variable = abc" ;;
  xyz)  echo "\$variable = xyz" ;;
esac
;;&, ;&

Caractères de fin dans une sélection par case (version 4+ de Bash). 

.

Commande « point » [point]. Équivalent au source (voir l'Exemple 15.22, « « Inclure » un fichier de données »). C'est une commande intégrée de Bash.

.

« point », comme composant d'un nom de fichier. Lors de l'utilisation de noms de fichiers, un point au début est le préfixe d'un fichier « caché », un fichier que ls ne montre habituellement pas.

bash$ touch .fichier_caché
bash$ ls -l
total 10
-rw-r--r--    1 bozo  bozo      4034 Jul 18 22:04 donnée1.carnet_d_adresses
-rw-r--r--    1 bozo  bozo      4602 May 25 13:58 donnée1.carnet_d_adresses.bak
-rw-r--r--    1 bozo  bozo       877 Dec 17  2000 boulot.carnet_d_adresse


bash$ ls -al
total 14
drwxrwxr-x    2 bozo  bozo      1024 Aug 29 20:54 ./
drwx------   52 bozo  bozo      3072 Aug 29 20:51 ../
-rw-r--r--    1 bozo  bozo      4034 Jul 18 22:04 donnée1.carnet_d_adresses
-rw-r--r--    1 bozo  bozo      4602 May 25 13:58 donnée1.carnet_d_adresses.bak
-rw-r--r--    1 bozo  bozo       877 Dec 17  2000 boulot.carnet_d_adresse
-rw-rw-r--    1 bozo  bozo         0 Aug 29 20:54 .fichier_caché
              

En ce qui concerne les noms de répertoires, un point représente le répertoire courant et deux points à la suite indiquent le répertoire parent.

 
bash$ pwd
/home/bozo/projets

bash$ cd .
bash$ pwd
/home/bozo/projets

bash$ cd ..
bash$ pwd
/home/bozo/
            

Le point apparaît souvent comme répertoire de destination d'une commande de mouvement de fichiers. Dans ce contexte, cela signifie le répertoire courant.

bash$ cp /home/bozo/travail_en_cours/débarras/* .
            

Copiez tous les fichiers du répertoire « débarras » dans $PWD.

.

Filtrage d'un caractère par le « point »Pour le filtrage de caractères au sein d'une expression rationnelle, un « point » correspond à un seul caractère.

"

Citation partielle [guillemets doubles]. "CHAÎNE" empêche l'interprétation de la plupart des caractères spéciaux présents dans la CHAÎNE. Voir le Chapitre 5, Guillemets et apostrophes.

'

Citation complète [guillemets simples]. 'CHAÎNE' empêche l'interprétation de tous les caractères spéciaux présents dans la CHAÎNE. Ces guillemets sont plus puissants que "CHAÎNE". Voir aussi le Chapitre 5, Guillemets et apostrophes.

,

Opérateur virguleL'opérateur virgule [16] relie une suite d'opérations arithmétiques. Toutes sont évaluées, mais seul le résultat de la dernière est renvoyé.

let "t2 = ((a = 9, 15 / 3))"  
# Initialise "a = 9" et "t2 = 15 / 3".

L'opérateur virgule sert aussi à accoler des chaînes de caractères.

for file in /{,usr/}bin/*calc
#             ^    Renvoie tous les fichiers exécutables se terminant
#+                 par "calc" dans les répertoires /bin et /usr/bin.
do
        if [ -x "$file" ]
        then
          echo $file
        fi
done

# /bin/ipcalc
# /usr/bin/kcalc
# /usr/bin/oidcalc
# /usr/bin/oocalc


# Merci à Rory Winston pour cette remarque.
, ,,

Conversion en minuscules dans une substitution de paramètres (à partir de la version 4 de Bash). 

\

Échappement [antislash]. Un mécanisme d'échappement pour les caractères seuls.

\x échappe le caractère x. Cela a pour effet de « mettre x entre guillemets », exactement comme 'x'. Le \ peut être utilisé pour mettre " et ' entre guillemets, ce qui permet de les écrire sous forme littérale.

Voir le Chapitre 5, Guillemets et apostrophes pour une explication plus détaillée des caractères échappés.

/

Séparateur dans le chemin d'un fichier [barre oblique]. Sépare les composants d'un nom de fichier (comme dans /home/bozo/projets/Makefile).

C'est aussi l'opérateur arithmétique de division.

`

Substitution de commandes [guillemet inversé]. L'expression `commande` rend la sortie de commande disponible pour l'affecter à une variable. On parle aussi de guillemets inversés.

:

Commande nulle [deux-points]. C'est l'équivalent shell d'un « NOP » (no op, c'est-à-dire « aucune opération »). On peut la voir comme un synonyme de la commande intégrée true. La commande « : » est elle-même une commande intégrée de Bash et son état de sortie est true (0).

:
echo $?   # 0

Boucle sans fin :

while :
do
   operation-1
   operation-2
   ...
   operation-n
done

# Identique à :
#    while true
#    do
#      ...
#    done

Bouche-trou dans un test if/then :

if condition
then :   # Ne rien faire et continuer
else     # Et sinon...
   faire_quelque_chose
fi

Bouche-trou quand une opération binaire est attendue, voir l'Exemple 8.2, « Utiliser les opérations arithmétiques » et les paramètres par défaut.

: ${nom_utilisateur=`whoami`}
# ${nom_utilisateur=`whoami`}   produit une erreur s'il manque les
#                               deux-points au tout début,
#                               sauf si "nom_utilisateur" est une
#                               commande ou une commande intégrée...

: ${1?"Usage: $0 ARGUMENT"}     # Tiré du script d'exemple "usage-message.sh".

Sert de bouche-trou quand une commande est attendue dans un document en ligne. Voir l'Exemple 19.10, « document intégré « anonyme » ».

Évalue une suite de variables en utilisant la substitution de paramètres (comme dans l'Exemple 10.7, « Utiliser le remplacement et les messages d'erreur »).

: ${HOSTNAME?} ${USER?} ${MAIL?}
#  Affiche un message d'erreur
#+ si une variable d'environnement (ou plusieurs) n'est pas initialisée.

Expansion de variable / remplacement d'une sous-chaîne.

En combinaison avec l'opérateur de redirection >, tronque un fichier à la taille zéro sans modifier ses droits. Crée le fichier s'il n'existait pas auparavant.

: > données.xxx   # Fichier "données.xxx" maintenant vide

# Même effet que cat /dev/null >données.xxx
# Néanmoins, cela ne crée pas un nouveau processus, car ":" est une commande intégrée.

Voir aussi l'Exemple 16.15, « Utiliser tail pour surveiller le journal des traces système ».

En combinaison avec l'opérateur de redirection >>, elle n'a pas d'effet sur un fichier cible déjà existant (: >> nouveau_fichier). Crée le fichier s'il n'existait pas.

[Note]

Note

Ce procédé s'applique aux fichiers réguliers mais pas aux tubes, et pas non plus aux liens symboliques ni à certains fichiers spéciaux.

Peut servir à commencer une ligne de commentaire bien que ce ne soit pas recommandé. Utiliser # pour un commentaire désactive la vérification d'erreur pour le reste de la ligne, donc vous pouvez y mettre pratiquement n'importe quoi. En revanche, ce n'est pas le cas avec :.

: Ceci est un commentaire qui génère une erreur, ( if [ $x -eq 3] ).

Le « : » sert de séparateur de champ, dans /etc/passwd et dans la variable $PATH.

bash$ echo $PATH
/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/sbin:/usr/sbin:/usr/games

Les deux-points constituent un nom de fonction valide.

:()
{
  echo "Le nom de cette fonction est "$NOMFONCT"
  # Pourquoi utiliser les deux-points comme nom de fonction ?
  # C'est juste un moyen pour écrire du code illisible...
}

:

# Le nom de cette fonction est :

Ce comportement n'est pas portable, par conséquent nous ne le recommandons pas.

!

Inverse le sens d'un test ou d'un état de sortie. L'opérateur ! inverse l'état de sortie de la commande à laquelle il est appliqué (voir l'Exemple 6.2, « Inverser une condition en utilisant ! »). Il inverse aussi la signification d'un opérateur de test. Par exemple, cela peut changer le sens d'un égal (=) en un différent ( != ). L'opérateur ! est un mot-clé Bash.

Dans un autre contexte, le ! apparaît aussi dans les références indirectes aux variables.

Dans un contexte encore différent, à partir de la ligne de commande, le ! appelle le mécanisme d'historique de Bash (voir l'Annexe K, Commandes d'historique). Notez que ce mécanisme est désactivé dans les scripts.

*

Joker [astérisque]. Le caractère * sert de « joker » pour l'expansion des noms de fichiers dans les remplacements. Utilisé seul, il correspond à tous les noms de fichier d'un répertoire donné.

bash$ echo *
abs-book.sgml add-drive.sh agram.sh alias.sh
              

L'astérisque * représente tout caractère répété un nombre quelconque de fois (ou pas du tout) dans une expression rationnelle.

*

Opérateur arithmétiqueDans le contexte des opérations arithmétiques, * indique la multiplication.

Le double astérisque ** représente soit l'opérateur exponentielle, soit un remplacement étendu de noms de fichiers.

?

Opérateur de test. À l'intérieur de certaines expressions, le ? indique un test pour une condition.

Dans une expression entre doubles parenthèses, ? peut servir comme élément d'un opérateur ternaire dans le style du langage C, ?:.

condition?resultat-si-condition-vraie:resultat-si-condition-fausse

(( var0 = var1<98?9:21 ))
#                ^ ^

# if [ "$var1" -lt 98 ]
# then
#   var0=9
# else
#   var0=21
# fi

Dans une expression de substitution de paramètres, le ? teste si une variable a été initialisée.

?

Joker. Le caractère ? sert de joker pour un seul caractère dans l'expansion d'un nom de fichier dans un remplacement, et représente également un caractère dans une expression rationnelle étendue.

$

Substitution de variable (contenu d'une variable). 

var1=5
var2=23skidoo

echo $var1     # 5
echo $var2     # 23skidoo

Un $ préfixant un nom de variable donne la valeur que contient cette variable.

$

Fin de ligne. Dans une expression rationnelle, un $ marque la fin d'une ligne de texte.

${}

Substitution de paramètres

$'...'

Développement de chaînes de caractères entre guillemetsCette syntaxe produit l'expansion de valeurs octales ou hexadécimales échappées une ou plusieurs fois, en valeurs ASCII [17] ou en caractères Unicode.

$*, $@

Paramètres de position

$?

Variable contenant l'état de sortie. La variable $? contient l'état de sortie d'une commande, d'une fonction ou d'un script.

$$

Variable contenant l'identifiant du processus. La variable $$ contient le PID [18] du script dans lequel elle apparaît.

()

Groupe de commandes. 

(a=bonjour; echo $a)
[Important]

Important

Une liste de commandes entre parenthèses lance un sous-shell.

Les variables comprises dans ces parenthèses, à l'intérieur du sous-shell, ne sont pas visibles par le reste du script. Le processus parent, le script, ne peut pas lire les variables créées dans le processus fils, le sous-shell.

a=123
( a=321; )

echo "a = $a"   # a = 123
# "a" à l'intérieur des parenthèses agit comme une variable locale.

Initialisation de tableaux. 

Tableau=(element1 element2 element3)

{xxx,yyy,zzz,...}

Expansion d'accolades. 

echo \"{Mots,entourés,de,guillemets}\"   # " avant et après
# "Mots" "entourés" "de" "guillemets"

cat {fichier1,fichier2,fichier3} > fichier_combiné
# Concatène les fichiers fichier1, fichier2 et fichier3 vers fichier_combiné.

cp fichier22.{txt,sauve}
# Copie "fichier22.txt" dans "fichier22.sauve"

Une commande peut agir sur une liste de fichiers séparés par des virgules entre des accolades [19]. L'expansion de noms de fichiers (remplacement) s'applique aux fichiers contenus dans les accolades.

[Attention]

Attention

Aucun espace n'est autorisé à l'intérieur des accolades, à moins qu'ils ne soient placés entre guillemets, ou échappés.

echo {fichier1,fichier2}\ :{\ A," B",' C'}

fichier1 : A fichier1 : B fichier1 : C fichier2 : A fichier2 : B fichier2 : C

{a..z}

Expansion étendue d'accolades. 

echo {a..z} # a b c d e f g h i j k l m n o p q r s t u v w x y z
# Affiche les caractères entre a et z.

echo {0..3} # 0 1 2 3
# Affiche les caractères entre 0 et 3.

caracteres_en_base64=( {A..Z} {a..z} {0..9} + / = )
# Initialisation d'un tableau par une expansion d'accolades.
# Tiré du script d'exemple "base64.sh" de vladz.

L'expression {a..z}, avec expansion étendue d'accolades est une fonctionnalité introduite dans la version 3 de Bash.

{}

Bloc de code [accolade]. On dit aussi groupe en ligne. Cette expression crée une fonction anonyme (une fonction sans nom). Néanmoins, contrairement à une fonction standard, les variables d'un bloc de code restent visibles par le reste du script.

bash$ { local a;
a=123; }
bash: local: utilisable seulement dans une fonction
a=123
{ a=321; }
echo "a = $a"   # a = 321   (valeur à l'intérieur du bloc de code)

# Merci à S.C.

Le bloc de code entouré par des accolades peut utiliser la redirection d'entrées/sorties.

Exemple 3.1. Blocs de code et redirection d'entrées/sorties

#!/bin/bash
# Lit les lignes de /etc/fstab.

Fichier=/etc/fstab

{
read ligne1
read ligne2
} < $Fichier

echo "La première ligne dans $Fichier est :"
echo "$ligne1"
echo
echo "La deuxième ligne dans $Fichier est :"
echo "$ligne2"

exit 0

# Maintenant, comment analysez-vous les champs séparés de chaque ligne ?
# Astuce : utilisez awk, ou...
# ... Hans-Joerg Diers suggère d'utiliser la commande set de Bash.

Exemple 3.2. Enregistrer la sortie d'un bloc de code dans un fichier

#!/bin/bash
# rpm-check.sh

#  Recherche une description à partir d'un fichier rpm, et s'il peut être
#+ installé.
#  Sauvegarde la sortie dans un fichier.
#
#  Ce script illustre l'utilisation d'un bloc de code.

SUCCES=0
E_SANSARGS=65

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

{ # Début du bloc de code
  echo
  echo "Description de l'archive :"
  rpm -qpi $1       # Requête pour la description.
  echo
  echo "Contenu de l'archive :"
  rpm -qpl $1       # Requête pour la liste.
  echo
  rpm -i --test $1  # Requête pour savoir si le fichier rpm est installable.
  if [ "$?" -eq $SUCCES ]
  then
    echo "$1 est installable."
  else
    echo "$1 n'est pas installable."
  fi  
  echo # Fin du bloc de code
} > "$1.test"       # Redirige la sortie de tout le bloc vers un fichier.

echo "Les résultats du test rpm sont dans le fichier $1.test"

# Voir la page de manuel de rpm pour des explications sur les options.

exit 0

[Note]

Note

Contrairement à un groupe de commandes entre parenthèses, un bloc de code entouré comme ci-dessus par des accolades ne sera, en principe, pas lancé dans un sous-shell. [20]

{}

Emplacement pour du texte. Utilisé après xargs -i (option de remplacement de chaîne). Les doubles accolades {} sont un emplacement pour du texte en sortie.

ls . | xargs -i -t cp ./{} $1
#            ^^         ^^

# Provient de l'exemple "ex42.sh" (copydir.sh).

{} \;

Chemin. Principalement utilisé pour les commandes find. Ce n'est pas une commande intégrée du shell.

[Note]

Note

Le « ; » termine l'option -exec d'une séquence de commandes find. Il a besoin d'être échappé pour que le shell ne l'interprète pas.

[ ]

Test. 

Teste l'expression entre crochets [ ]. Notez que [ fait partie de la commande intégrée test (et en est un synonyme), ce n'est pas un lien vers la commande externe /usr/bin/test.

[[ ]]

Test. 

Teste l'expression entre doubles crochets [[ ]]. C'est un mot-clé du shell.

Voir les explications sur la syntaxe [[ ... ]].

[ ]

Élément d'un tableau. 

Accolés au nom d'un tableau, les crochets indiquent l'indice d'un élément.

Tableau[1]=slot_1
echo ${Tableau[1]}
[ ]

Ensemble de caractères. 

Dans une expression rationnelle, les crochets désignent un ensemble de caractères devant servir de motif (N.d.T : cet ensemble peut être un intervalle).

$[ ... ]

expansion d'entiers. 

Évalue l'expression entière entre $[ ].

a=3
b=7

echo $[$a+$b]   # 10
echo $[$a*$b]   # 21

NB : cette syntaxe, à présent obsolète, a été remplacée par la syntaxe (( ... )).

(( ))

Expansion d'entiers. 

Développe et évalue une expression entière entre doubles parenthèses (( )).

Voir les explications sur la structure (( ... )).

> &> >& >> < <>

Redirection

nom_script >nom_fichier redirige la sortie de nom_script vers le fichier nom_fichier et écrase nom_fichier s'il existe déjà.

commande &>nom_fichier redirige à la fois stdout et stderr de commande vers nom_fichier.

[Note]

Note

C'est utile pour supprimer tous les messages de sortie quand on teste une condition. Voyons par exemple si une certaine commande existe :

bash$ type commande_erronee &>/dev/null

                
bash$ echo $?
1
              

Ou dans un script:

test_de_commande () { type "$1" &>/dev/null; }
#                                          ^
            
cmd=rmdir            # Commande valide.
test_de_commande $cmd; echo $?   # 0
            
cmd=commande_erronee    # Commande non valide.
test_de_commande $cmd; echo $?   # 1

commande >&2 redirige stdout de commande vers stderr.

nom_script >>nom_fichier ajoute la sortie de nom_script à la fin du fichier nom_fichier. Si le fichier n'existe pas déjà, il est créé.

[i]<>nom_fichier ouvre le fichier nom_fichier en lecture et écriture, et lui affecte le descripteur de fichier i. Si le fichier nom_fichier n'existe pas encore, alors il est créé.

Substitution de processus

(commande)>

<(commande)

Dans un autre contexte, les caractères < et > agissent comme des opérateurs de comparaison de chaînes de caractères.

Dans un contexte encore différent, les caractères < et > agissent comme des opérateurs de comparaison d'entiers. Voir aussi l'Exemple 16.9, « Utiliser expr ».

<<

Redirection utilisée dans un document en ligne

<<<

Redirection utilisée dans une chaîne en ligne

<, >

Comparaison ASCII

leg1=carottes
leg2=tomates

if [[ "$leg1" < "$leg2" ]]
then
  echo -n "Le fait que $leg1 précède $leg2 dans le dictionnaire "
  echo "n'a rien à voir avec mes préférences culinaires."
else
  echo "Mais quel type de dictionnaire utilisez-vous?"
fi
\< ... \>

Délimitation d'un mot dans une expression rationnelle

bash$ grep '\<mot\>' fichier_texte

|

Tube. Passe la sortie (stdout) de la commande précédente à l'entrée (stdin) de la suivante ou au shell. Cette méthode permet d'enchaîner les commandes.

echo ls -l | sh
#  Passe la sortie de "echo ls -l" au shell
#+ avec le même résultat qu'un simple "ls -l".


cat *.lst | sort | uniq
#  Assemble et trie tous les fichiers ".lst", puis supprime les lignes
#+ dupliquées.

La sortie d'une ou plusieurs commandes peut être envoyée à un script via un tube.

#!/bin/bash
# uppercase.sh : Convertit l'entrée en majuscules.

tr 'a-z' 'A-Z'
#  La plage de lettres doit être placée entre guillemets pour empêcher que la
#+ génération des noms de fichiers ne se fasse uniquement sur les noms
#+ de fichier d'un caractère de long.

exit 0

Maintenant, envoyons par le tube la sortie de ls -l à ce script.

bash$ ls -l | ./uppercase.sh
-RW-RW-R--    1 BOZO  BOZO       109 APR  7 19:49 1.TXT
-RW-RW-R--    1 BOZO  BOZO       109 APR 14 16:48 2.TXT
-RW-R--R--    1 BOZO  BOZO       725 APR 20 20:56 FICHIER-DONNEES
              
[Note]

Note

Le canal stdout de chaque processus dans un tube doit pouvoir être lu comme canal stdin par le suivant. Si ce n'est pas le cas, le flux de données va se bloquer et le tube ne se comportera pas comme prévu.

cat fichier1 fichier2 | ls -l | sort
# La sortie à partir de "cat fichier1 fichier2" disparaît.

Un tube tourne en tant que processus fils et ne peut donc modifier les variables du script.

variable="valeur_initiale"
echo "nouvelle_valeur" | read variable
echo "variable = $variable"     # variable = valeur_initiale

Si une des commandes du tube échoue, l'exécution du tube se termine prématurément. Dans ces conditions, on a un tube cassé et on envoie un signal SIGPIPE.

>|

Force une redirection (même si l' option noclobber est activée). Ceci va forcer l'écrasement d'un fichier déjà existant.

||

Opérateur logique OUDans une structure de test , l'opérateur || a comme valeur de retour 0 (succès) si l'une des conditions est vraie.

&

Exécuter la tâche en arrière-plan. Une commande suivie par un & fonctionnera en tâche de fond.

bash$ sleep 10 &
[1] 850
[1]+  Done                    sleep 10
              

À l'intérieur d'un script, les commandes et même les boucles peuvent tourner en tâche de fond.

Exemple 3.3. Exécuter une boucle en tâche de fond

#!/bin/bash
# background-loop.sh

for i in 1 2 3 4 5 6 7 8 9 10            # Première boucle.
do
  echo -n "$i "
done & # Exécute cette boucle en tâche de fond.
       # S'exécutera parfois après la deuxième boucle.

echo   # Cet 'echo' ne s'affichera pas toujours.

for i in 11 12 13 14 15 16 17 18 19 20   # Deuxième boucle.
do
  echo -n "$i "
done

echo   # Cet 'echo' ne s'affichera pas toujours.

# ======================================================

# Sortie attendue de ce script :
# 1 2 3 4 5 6 7 8 9 10 
# 11 12 13 14 15 16 17 18 19 20 

# Mais parfois, vous obtiendrez :
# 11 12 13 14 15 16 17 18 19 20 
# 1 2 3 4 5 6 7 8 9 10 bozo $
# (Le deuxième 'echo' ne s'exécute pas. Pourquoi ?)

# Occasionnellement aussi :
# 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
# (Le premier 'echo' ne s'exécute pas. Pourquoi ?)

# Et très rarement :
# 11 12 13 1 2 3 4 5 6 7 8 9 10 14 15 16 17 18 19 20
# La boucle en avant plan s'exécute avant celle en tâche de fond.

exit 0

#  Nasimuddin Ansari suggère d'ajouter   sleep 1
#+ après le   echo -n "$i"   aux lignes 6 et 14,
#+ pour s'amuser un peu.

[Attention]

Attention

Une commande exécutée en tâche de fond à l'intérieur d'un script peut faire se suspendre l'exécution, attendant l'appui sur une touche. Heureusement, il est possible d'y remédier.

&&

Opérateur logique ETDans une structure de test, l'opérateur && renvoie 0 (succès) si et seulement si les deux conditions sont vraies.

-

Préfixe d'options. Introduit les options pour les commandes ou les filtres. Sert aussi de préfixe pour des opérateurs et pour les paramètres par défaut dans les substitutions de paramètres.

COMMANDE -[Option1][Option2][...]

ls -al

sort -dfu $nom_fichier

if [ $fichier1 -ot $fichier2 ]
then #         ^
  echo "Le fichier $fichier1 est plus ancien que le $fichier2."
fi

if [ "$a" -eq "$b" ]
then #    ^
  echo "$a est égal à $b."
fi

if [ "$c" -eq 24 -a "$d" -eq 47 ]
then #    ^              ^
  echo "$c vaut 24 et $d vaut 47."
fi


param2=${param1:-$DEFAULTVAL}
#               

--

Le tiret double -- est le préfixe des options longues pour les commandes.

sort --ignore-leading-blanks

Utilisé avec une commande interne Bash, il signifie la fin des options de cette commande spécifique.

[Astuce]

Astuce

Ceci donne un moyen simple pour supprimer les fichiers dont les noms commencent avec un tiret.

bash$ ls -l
-rw-r--r-- 1 bozo bozo 0 Nov 25 12:29 -vilainnom


bash$ rm -- -vilainnom

bash$ ls -l
total 0

Le double-tiret est aussi utilisé avec set.

set -- $variable (comme dans Exemple 15.18, « Réaffecter les paramètres de position »)

-

Redirection depuis/vers stdin ou stdout [tiret]. 

bash$ cat -
                abc
                abc
                
                ...
                
                Ctl-D

Comme prévu, cat - lit sur stdin -- ici la saisie au clavier de l'utilisateur au clavier -- et renvoie ce qu'il a lu vers stdout. Mais la redirection des entrées/sorties avec - a-t-elle des applications dans le monde réel ?

(cd /source/répertoire && tar cf - . ) | (cd /dest/répertoire && tar xpvf -)
# Déplace l'ensemble des fichiers d'un répertoire vers un autre
# [reproduit avec l'autorisation d'Alan Cox <a.cox@swansea.ac.uk>,
# après de légères modificcations]

# 1) cd /source/répertoire
#    Répertoire source, où se trouvent les fichiers à déplacer.
# 2) &&
#    "liste ET": si l'opération 'cd' a fonctionné,
#    donc on exécute la commande suivante.
# 3) tar cf - .
#    L'option 'c' de la commande d'archivage 'tar' crée une nouvelle archive,
#    l'option 'f' (fichier), suivie par '-' désigne stdout comme fichier cible.
#    et la compression se fait dans le répertoire courant ('.').
# 4) |
#    Enchaîne avec...
# 5) ( ... )
#    Un sous-shell.
# 6) cd /dest/répertoire
#    Se déplace dans le répertoire de destination.
# 7) &&
#    "liste ET", comme ci-dessus.
# 8) tar xpvf -
#    Déballe l'archive ('x'), préserve l'appartenance
#    et les droits des fichiers ('p'),
#    puis envoie de nombreux messages vers stdout ('v'),
#    en lisant les données provenant de stdin
#    ('f' suivi par un '-').
#
#    Remarque : 'x' est une commande, tandis que 'p', 'v', 'f' 
#    sont des options.
# Ouf !


# Ècriture équivalente, en moins élégant :
#   cd /source/répertoire
#   tar cf - . | (cd ../dest/répertoire; tar xpvf -)
#
#     Même effet également :
# cp -a /source/répertoire/* /dest/répertoire
#     Et aussi :
# cp -a /source/répertoire/* /source/répertoire/.[^.]* /dest/répertoire
#     S'il y a des fichiers cachés dans /source/répertoire.
bunzip2 -c linux-2.6.16.tar.bz2 | tar xvf -
#  --décompresse l'archive--  | --puis la passe à "tar"--
#  Si "tar" n'a pas intégré le correctif de support de "bunzip2",
#+ il faut procéder en deux étapes distinctes avec un tube.
#  Le but de cet exercice est de désarchiver les sources du noyau compressées
#+ avec bzip2.

Remarque : dans ce contexte, le signe « - » n'est pas en lui-même un opérateur Bash, mais plutôt une option reconnue par quelques utilitaires UNIX qui écrivent dans stdout ou lisent dans stdin, comme tar, cat, ...

bash$ echo "quelque chose" | cat -
quelque chose 

Lorsqu'un nom de fichier est attendu, un - redirige la sortie vers stdout (vous pouvez le rencontrer avec tar cf), ou accepte une entrée de stdin au lieu d'un fichier. Voici une méthode d'utilisation d'un outil de manipulation de fichiers comme filtre dans un tube :

bash$ file
Usage: file [-bciknvzL] [-f namefile] [-m magicfiles] file...
              

En ligne de commande, un file nu échoue avec un message d'erreur.

Pour un résultat utilisable, on peut ajouter un « - » : alors le shell attend une entrée de l'utilisateur.

bash$ file -
abc
standard input:              ASCII text


bash$ file -
#!/bin/bash
standard input:              Bourne-Again shell script text executable
            

À présent, la commande accepte une entrée de stdin et l'analyse.

Le « - » peut être utilisé pour faire passer la sortie standard stdout à d'autres commandes via un tube, ce qui permet quelques astuces comme l'ajout de lignes au début d'un fichier.

Par exemple, vous pouvez utiliser diff pour comparer un fichier avec une partie d'un autre fichier :

grep Linux fichier1 | diff fichier2 -

Finalement, un exemple réel utilisant - avec tar.

Exemple 3.4. Sauvegarde de tous les fichiers modifiés depuis 24 heures

#!/bin/bash

#  Sauvegarde dans une archive tar compressée tous les fichiers
#+ du répertoire courant modifiés dans les dernières 24 heures.

FICHIERARCHIVE=archive-$(date +%m-%d-%Y)
#                 Intégration de la date dans le nom du fichier de sauvegarde.
#                 Merci pour cette idée, Joshua Tschida.
archive=${1:-$FICHIERARCHIVE}
#  Si aucun nom de fichier n'est spécifié sur la ligne de commande,
#+ le script utilisera "archive-MM-JJ-AAAA.tar.gz par défaut."

tar cvf - `find . -mtime -1 -type f -print` > $archive.tar
gzip $archive.tar
echo "Répertoire $PWD sauvegardé dans un fichier archive \"$archive.tar.gz\"."

#  Stephane Chazelas indique que le code ci-dessus échoue s'il trouve
#+ trop de fichiers, ou si un des noms de fichier contient des blancs.

# Il suggère d'utiliser plutôt&nbsp;:
# -------------------------------------------------------------------
#   find . -mtime -1 -type f -print0 | xargs -0 tar rvf "$archive.tar"
#         avec la version GNU de "find".

#   find . -mtime -1 -type f -exec tar rvf "$archive.tar" '{}' \;
#         portable sur les autres UNIX, mais plus lent.
# -------------------------------------------------------------------

exit 0

  

[Attention]

Attention

Les noms de fichiers commençant avec un « - » peuvent poser problème lorsqu'ils sont couplés avec l'opérateur de redirection « - ». Votre script doit détecter de tels fichiers et leur ajouter un préfixe approprié, par exemple ./-NOMFICHIER, $PWD/-NOMFICHIER, ou $NOMCHEMIN/-NOMFICHIER.

Il y aura probablement un problème si la valeur x d'une variable commence avec un -.

var="-n"
echo $var
# A le même effet qu'un "echo -n" et donc n'affiche rien.
-

Répertoire précédent. cd - revient au répertoire précédent en utilisant la variable d'environnement $OLDPWD.

[Attention]

Attention

Ne confondez pas « - » utilisé dans ce sens avec l'opérateur de redirection « - » vu précédemment. L'interprétation du « - » dépend du contexte dans lequel il apparaît.

-

Moins. Le signe moins indique l'opération arithmétique.

=

Égal. Opérateur d'affectation.

a=28
echo $a   # 28

Dans un autre contexte, le signe = est un opérateur de comparaison de chaînes de caractères.

+

Plus. Opérateur arithmétique d'addition.

Dans un autre contexte, le + est un opérateur d'expression rationnelle.

+

Option. Option pour une commande ou un filtre.

Certaines commandes, intégrées ou non, utilisent le + pour activer certaines options et le - pour les désactiver. Dans la substitution de paramètres, le + préfixe une autre valeur qu'une variable étend.

%

Modulo. Opérateur arithmétique modulo (reste d'une division entière).

let "z = 5 % 3"
echo $z  # 2

Dans un autre contexte, le % est un opérateur de reconnaissance de motifs.

~

Répertoire de l'utilisateur [tilde]. Le ~ correspond à la variable interne $HOME. ~bozo est le répertoire de l'utilisateur bozo et ls ~bozo liste son contenu. ~/ est le répertoire de l'utilisateur courant et ls ~/ liste son contenu.

bash$ echo ~bozo
/home/bozo

bash$ echo ~
/home/bozo

bash$ echo ~/
/home/bozo/

bash$ echo ~:
/home/bozo:

bash$ echo ~utilisateur-inexistant
~utilisateur-inexistant
              

~+

Répertoire courant. Correspond à la variable interne $PWD.

~-

Répertoire courant précédent. Correspond à la variable interne $OLDPWD.

=~

correspondance d'une expression rationnelleCet opérateur a été introduit avec la version 3 de Bash.

^

Début de ligne. Dans une expression rationnelle, un « ^ » correspond au début d'une ligne de texte.

^, ^^

Conversion en majuscules dans une substitution de paramètres (à partir de la version 4 de Bash). 

Caractères de contrôle

Modifient le comportement d'un terminal ou de l'affichage d'un texte. Un caractère de contrôle est une combinaison CONTROL + touche (appuyés simultanément). Un caractère de contrôle peut aussi être écrit en notation octale ou hexadécimale, après un échappement.

Normalement, on n'utilise pas les caractères de contrôle à l'intérieur d'un script.

  • Ctl-A

    Déplace le curseur au début de la ligne de texte (en ligne de commande).

  • Ctrl-B

    Retour en arrière (backspace) non destructif.

  • Ctrl-C

    Termine un job en avant-plan.

  • Ctrl-D

    Déconnecte du shell (comme exit).

    C'est le caractère EOF (End Of File, fin de fichier), qui termine aussi l'entrée de stdin.

    Lors de la saisie de texte sur la console ou dans une fenêtre xterm, Ctl-D efface le caractère sous le curseur. Quand aucun caractère n'est présent, Ctl-D vous déconnecte de la session. Dans un terminal graphique, ceci a pour effet de fermer la fenêtre.

  • Ctl-E

    Déplace le curseur à la fin de la ligne de texte (en ligne de commande).

  • Ctl-F

    Déplace le curseur d'un caractère vers l'avant (en ligne de commande).

  • Ctrl-G

    CLOCHE (bip). Sur quelques anciens terminaux comme les télétypes, cette touche fait sonner une vraie cloche. Dans un xterm, elle fait parfois biper.

  • Ctrl-H

    Gommage Supprime le caractère précédent (Backspace). Efface les caractères survolés à reculons par le curseur.

    #!/bin/bash
    # Ctrl-H dans une chaîne de caractères.
    
    a="^H^H"                   # Deux Ctrl-H (backspaces)
                               # Ctrl-V Ctrl-H si vous utilisez vi/vim
    echo "abcdefg"             # abcdefg
    echo
    echo -n "abcdefg$a "       # abcd fg
    # Espace à la fin ^              ^ Deux fois Backspace.
    echo
    echo -n "abcdef$a"         # abcdef
    #  Pas d'espace à la fin          ^ Ne fait pas de backspace (pourquoi?).
                               # Les résultats ne sont pas toujours
                               #+ conformes aux prévisions
    echo; echo
    
    # Constantin Hagemeier suggère d'essayer :
    # a=$'\010\010'
    # a=$'\b\b'
    # a=$'\x08\x08'
    # mais cela ne modifie pas les résultats.
    
    ########################################
    
    # À présent, essayez ceci :
    
    gommage="^H^H^H^H^H"       # 5 x Ctl-H.
    
    echo -n "12345678"
    sleep 2
    echo -n "$gommage"
    sleep 2
    
  • Ctrl-I

    Tabulation horizontale.

  • Ctrl-J

    Saut de ligne (line feed). Dans un script, on peut aussi l'écrire en octal -- '\012' ou en hexadécimal -- '\x0a'.

  • Ctrl-K

    Tabulation verticale.

    Lors de la saisie de texte sur la console ou dans une fenêtre xterm, Ctl-K efface les caractères depuis le curseur jusqu'à la fin de la ligne. Dans un script, Ctl-K se comporte parfois différemment, comme dans l'exemple ci-dessous de Lee Maschmeyer.

  • Ctrl-L

    Saut de page (efface l'écran du terminal). Dans un terminal, cette combinaison de touches produit le même effet que la commande clear. Si on l'envoie à une imprimante, Ctl-L éjecte la page de papier.

  • Ctrl-M

    Retour chariot.

    #!/bin/bash
    # Merci à Lee Maschmeyer pour cet exemple.
    
    read -n 1 -s -p \
      $'Control-M ramène le curseur au début de la ligne. Tapez Enter. \x0d'
                                      #  Bien sûr, '0d' est l'équivalent en
                                      #+ hexadécimal de Control-M.
    echo >&2   #  Le '-s' rend la frappe invisible, donc il est nécessaire d'aller
               #+ explicitement sur la nouvelle ligne.
    
    read -n 1 -s -p $'Control-J place le curseur au début de la ligne suivante. \x0a'
               #  '0a' est l'équivalent hexadécimal de Control-J, le saut de ligne.
    echo >&2
    
    ###
    
    read -n 1 -s -p $'Et Control-K\x0bva juste en-dessous.'
    echo >&2   #  Control-K est la tabulation verticale.
    
    # Pour un meilleur exemple d'effet de la tabulation verticale :
    
    var=$'\x0aVoici la ligne du dessous\x0bVoici la ligne du dessus\x0a'
    echo "$var"
    #  Fonctionne de la même façon que l'exemple ci-dessus. Néanmoins :
    echo "$var" | col
    #  Fait en sorte que la fin de ligne de droite se place plus haut que
    #  la gauche. 
    #  Voilà pourquoi nous avons commencé et terminé par un retour
    #+ chariot : pour un affichage moins confus.
    
    # Comme l'explique Lee Maschmeyer :
    # --------------------------
    #  Dans le [premier exemple de tabulation verticale]... la tabulation verticale
    #+ déplace juste l'affichage en-dessous, sans retour chariot.
    #  C'est vrai seulement sur les périphériques, comme la console Linux, qui ne
    #+ peuvent pas aller à reculons.
    #  Le véritable but de VT est d'aller directement au dessus, et non au dessous.
    #  Cela peut être utilisé sur une imprimante.
    #  L'utilitaire col peut s'utiliser en émulation du comportement de VT.
    
    exit 0
    
  • Ctl-N

    Supprime une ligne de texte rappelée à partir du tampon de l'historique [22] (en ligne de commande).

  • Ctl-O

    Produit un retour à la ligne (en ligne de commande).

  • Ctl-P

    Rappelle la dernière commande depuis le tampon d'historique (en ligne de commande).

  • Ctrl-Q

    Sort du mode pause du terminal (XON).

    Cette combinaison de touches réactive le stdin du terminal après qu'il ait été mis en pause.

  • Ctl-R

    Recherche arrière pour le texte dans le tampon historique (sur la ligne de commande).

  • Ctrl-S

    Pause du terminal (XOFF).

    Cette combinaison des touches gèle le stdin du terminal (utilisez Ctrl-Q pour en sortir).

  • Ctrl-T

    Échange la position du caractère sous le curseur avec celle du caractère précédent (en ligne de commande).

  • Ctrl-U

    Efface une ligne de l'entrée depuis le début de la ligne jusqu'à la position du curseur. Avec certains paramétrages, Ctrl-U efface la ligne d'entrée entière, quelque soit la position du curseur.

  • Ctrl-V

    Lors d'une saisie de texte, Ctl-V permet l'insertion de caractères de contrôle. Par exemple, les deux lignes suivantes sont équivalentes :

    echo -e '\x0a'
    echo <Ctl-V><Ctl-J>
    

    Ctrl-V est surtout utile dans un éditeur de texte.

  • Ctrl-W

    Lors de la saisie d'un texte dans une console ou un terminal graphique, Ctrl-W efface les caractères à partir du curseur, à reculons jusqu'au premier blanc. Avec certains paramétrages, Ctrl-W efface à reculons jusqu'au premier caractère non alphanumérique.

  • Ctrl-X

    Dans certains traitements de texte, coupe le texte surligné et le place dans le presse-papier.

  • Ctrl-Y

    Colle le texte à l'endroit où il a été supprimé (avec Ctrl-U ou Ctrl-W).

  • Ctrl-Z

    Met en pause un job en avant-plan.

    Opération de substitution dans certains traitements de texte.

    Caractère EOF (end-of-file) dans le système de fichiers MSDOS.

Blanc

Fonctionne comme un séparateur entre les commandes et/ou les variables. Les blancs peuvent être des espaces, des tabulations, des lignes blanches ou d'une combinaison de tout cela. [23] Dans certains contextes, tels que les affectations de variable, les espaces blancs ne sont pas permis et sont considérés comme une erreur de syntaxe.

Les lignes blanches n'ont aucun effet sur l'action d'un script et sont donc utiles pour séparer visuellement les différentes parties.

La variable $IFS est une variable spéciale définissant pour certaines commandes le séparateur des champs en entrée. Sa valeur par défaut est l'espace.

Pour conserver les espaces à l'intérieur d'une chaîne de caractères ou d'une variable, utilisez des guillemets("").

Les filtres UNIX peuvent cibler et opérer sur les espaces en utilisant la classe de caractères POSIX [:space:].



[16] Un opérateur est un agent qui exécute une opération. L'exemple habituel est l'opérateur arithmétique, + - * /. Avec Bash, il y a croisement entre les concepts d'opérateur et de mots clés.

[17]

American Standard Code for Information Interchange. C'est un système d'encodage des caractères de texte (alphabétiques, numériques, et un ensemble limité de symboles) comme nombres à 7 bits pouvant être stockés et manipulés par les ordinateurs. La plupart des caractères ASCII sont présents sur le clavier standard.

[18] Un PID, ou identifiant de processus, est un numéro affecté à un processus en cours d'exécution. Les PID des processus sont visibles avec la commande ps.

Définition : un processus est un programme en cours d'exécution. On dit parfois un job.

[19] Le shell fait l'expansion des accolades. La commande elle-même agit sur le résultat de cette expansion.

[20] Exception : un bloc de code entre accolades dans un tube peut être lancé comme sous-shell.

ls | { read ligne1; read ligne2; }
#  Erreur. Le bloc de code entre accolades tourne comme un sous-shell,
#+ donc la sortie de "ls" ne peut être passée aux variables de ce bloc.
echo "La première ligne est $ligne1; la seconde ligne est $ligne2"  # Ne fonctionnera pas.

# Merci à S.C.

[21] De même que, dans l'ancien temps, le mot philtre désignait une potion dotée de pouvoirs de transformation magiques, un filtre UNIX transforme sa cible d'une façon (à peu près) analogue. (Si un programmeur pouvait réaliser un « philtre d'amour » qui fonctionne sur une machine Linux, il aurait toutes les chances de recevoir honneurs et consécration.)

[22] Bash stocke la liste des commandes précédemment lancées en ligne de commande dans un tampon, ou dans un espace en mémoire, pour pouvoir retrouver l'historique à partir des commandes internes de l'historique.

[23] Un saut de ligne (newline) est aussi une espace. Voilà pourquoi une ligne blanche, qui consiste seulement en un saut de ligne, est considérée comme une espace.