Ces structures algorithmiques sont utilisées dans les scripts shells. Pour rédiger un script shell (ici, en tcsh), il faut :
Créer le fichier avec un éditeur de texte (
sample.sh). La première ligne doit commencer avec le prologue [shebang] : #!/bin/tcsh.Lui attribuer la permission d'exécution sur le fichier créé (
chmod +x sample.sh).Enfin, pour l'exécuter il suffit de taper le nom du fichier, puisque c'est désormais une nouvelle commande (si on est dans le répertoire courant de cette commande, il suffit de taper
./sample.sh).
Nous donnons dans ce qui suit, quelques exemples de scripts shells (csh).
Cette structure permet l'exécution conditionnelle d'une instruction. Voici sa syntaxe :
if ( <condition> ) then <liste instructions> else if ( <condition> ) then <liste instructions> else if ( <condition> ) then <liste instructions> else <liste instructions> endif
Exemple 5. Utilisation du if
#!/bin/csh
# /home/pascal/bin/fcomp.sh
#
# but : comparer deux fichiers
#
# [Sept. 2002]
if ( ${#argv} != 2 ) then
echo "usage : $0 file1 file2"
echo "compare deux fichiers file1 et file2 et dit s'ils sont identiques ou \
differents."
exit -1
endif
cmp $argv[1] $argv[2] >& /dev/null
set ret = $?
if ( $ret == 0 ) then
echo "Les fichiers $argv[1] et $argv[2] sont identiques."
else if ( $ret == 1 ) then
echo "Les fichiers $argv[1] et $argv[2] sont differents."
else
echo "Une erreur est survenue !"
endif
exit $ret
Exemple 6. Résultat du code de Exemple 5, « Utilisation du if »
$ ~/bin/fcomp.sh usage : /home/pascal/bin/fcomp.sh file1 file2 compare deux fichiers file1 et file2 et dit s'il sont identiques ou differents. $ ~/bin/fcomp.sh ~/bin/fdate.sh ~/bin/fcomp.sh Les fichiers /home/pascal/bin/fdate.sh et /home/pascal/bin/fcomp.sh sont differe nts. $ ~/bin/fcomp.sh ~/bin/fdate.sh /etc/shadow Une erreur est survenue !
Cette structure permet l'exécution conditionnelle d'une instruction.
switch ( <variable> )
case motif1 :
<liste instructions>
breaksw
case motif2 :
case motif3 :
<liste instructions>
breaksw
case motif4 :
<liste instructions>
case motif5 :
<liste instructions>
default:
<liste instructions>
endsw
Exemple 7. Utilisation du switch
#!/bin/csh
# /home/pascal/bin/fdate.sh
#
# but : conversion date en francais
#
# [Sept. 2002]
set res=`date`
# Traiter le jour
switch ( $res[1] )
case Mon :
set day=Lun
breaksw
case Tue :
set day=Mar
breaksw
case Wed :
set day=Mer
breaksw
case Thu :
set day=Jeu
breaksw
case Fri :
set day=Ven
breaksw
case Sat :
set day=Sam
breaksw
case Sun :
set day=Dim
breaksw
endsw
# Traiter le mois
switch ( $res[2] )
case Feb :
set mon=Fev
breaksw
case Apr :
set mon=Avr
breaksw
case May :
set mon=Mai
breaksw
case Jun :
set mon=Juin
breaksw
case Jul :
set mon=Juil
breaksw
case Aug :
set mon=Aou
breaksw
case Jan :
case Sep :
case Oct :
case Nov :
case Dec :
set mon=$res[2]
breaksw
endsw
# Afficher le resultat
echo "$day $res[3] $mon $res[6], $res[4] [$res[5]]"
Exemple 8. Résultat du code de Exemple 7, « Utilisation du switch »
$ fdate.sh Sam 12 Avr 2003, 21:20:45 [RET]
Il s'agit de l'instruction standard qui permet d'exécuter le corps de la boucle tant que la condition d'entrée est vérifiée (valeur évaluée à vrai).
while ( <condition> ) <corps_de_boucle> end
Exemple 9. Utilisation du while
#!/bin/csh # ~/bin/test.sh set ind=1 while ( $ind < 11 ) @ indcar = $ind * $ind echo $ind " " $indcar @ ind++ end
Exemple 10. Résultat du code de Exemple 9, « Utilisation du while »
$ ~/bin/test.sh 1 1 2 4 3 9 4 16 5 25 6 36 7 49 8 64 9 81 10 100
Cette instruction permet de répéter un nombre entier de fois une même commande.
repeat <nombre_de_fois> <commande>
Exemple 11. Utilisation et résultat du repeat
$ repeat 3 echo "hello, world !" hello, world ! hello, world ! hello, world !
Cette instruction permet de traiter en séquence tous les éléments d'une liste.
foreach <variable> ( <liste_de_valeur> ) <corps_de_boucle> end
Exemple 12. Utilisation du foreach
#!/bin/csh # ~/bin/test.sh foreach i ( 1 2 3 4 5 6 7 8 9 ) @ m = $i * 2 echo -n " $m" end echo
Exemple 13. Résultat du code de Exemple 12, « Utilisation du foreach »
$ ~/bin/test.sh 2 4 6 8 10 12 14 16 18 $
Ce sont les variables que les utilisateurs peuvent définir. Le shell étant un langage interprété non typé, aucune déclaration n'est nécessaire et la même variable peut contenir tour à tour une chaîne de caractères, un entier, ou un vecteur (tableau d'éléments de type chaîne de caractères ou entier, indexé par des entiers, commençant à 1).
Affectation. Cette opération permet d'associer un contenu à une variable (le contenant). En csh comme en tcsh, l'affectation se fait en utilisant la commande interne set de la manière suivante :
EXEMPLE :
$ set myvar1 = 10
$ set myvar2 = "chaine de caractère"
$
Accès au contenu. L'accès au contenu de la variable se fait en préfixant le nom de la variable par le symbole $.
EXEMPLE (SUITE) :
$ echo $myvar1
10
$ echo $myvar2
chaine de caractère
$ echo myvar1 # omission du méta-caractère $
myvar1 # echo affiche le mot myvar1
On peut aussi encadrer la variable par les symboles { et } avant de la préfixer par le symbole $ :
EXEMPLE (SUITE) :
$ set si = "pas "
$ echo "c'est vraiment $sidrôle"
sidrôle: Undefined variable. # le $ porte ici sur le mot sidrôle et non sur si
$ echo "c'est vraiment ${si}drôle"
c'est vraiment pas drôle
$
Note
L'accès à une variable non définie produit (sauf rares exceptions) une erreur (en csh, comme en tcsh).
Effacer une variable. Cette opération permet de libérer la case mémoire occupée par la variable. Cela se fait en csh comme en tcsh, en utilisant la commande interne unset de la manière suivante :
EXEMPLE (SUITE) :
$ echo $myvar1
10
$ unset myvar1
$ echo $myvar1
myvar1: Undefined variable.
$
Variable de type vecteur. Voici un exemple :
CODE : #!/bin/csh # ~/bin/testvect.sh set vect = ( 2 -2 4 -4 6 -6 8 -8 10 -10 ) # afficher les éléments du vecteur vect, élément par élément set i = 1 set sum = 0 echo " - Affichage élément par élément" while ( $i <= ${#vect} ) echo '$vect[' $i '] = ' $vect[$i] @ sum = $sum + $vect[$i] @ i++ end echo " - Somme des elements = " $sum set j = 5 echo "Voici le vecteur vect (${#vect} éléments) : " $vect[1-] echo "Voici les $j premiers éléments du vecteur : " $vect[-$j] echo "Voici les derniers éléments à partir du $j e : " $vect[$j-] # fin du script EXEMPLE : $ ./testvect.sh - Affichage élément par élément $vect[ 1 ] = 2 $vect[ 2 ] = -2 $vect[ 3 ] = 4 $vect[ 4 ] = -4 $vect[ 5 ] = 6 $vect[ 6 ] = -6 $vect[ 7 ] = 8 $vect[ 8 ] = -8 $vect[ 9 ] = 10 $vect[ 10 ] = -10 - Somme des elements = 0 Voici le vecteur vect (10 éléments) : 2 -2 4 -4 6 -6 8 -8 10 -10 Voici les 5 premiers éléments du vecteur : 2 -2 4 -4 6 Voici les derniers éléments à partir du 5 e : 6 -6 8 -8 10 -10 $
Modificateurs de variables. Les modificateurs [modifiers] suivants peuvent être appliqués aux variables. On travaillera avec l'exemple (un vecteur) suivant :
$ set mypath = ( /usr/local/src/foo.c /usr/src/bar.cc ) # un vecteur de path
Tableau 8. Clé de modification
| Clé | Sémantique | Exemple | |||
|---|---|---|---|---|---|
|
|
$ echo $mypath:h /usr/local/src /usr/src/bar.cc $ echo $mypath:gh /usr/local/src /usr/src | |||
|
|
$ echo $mypath:t foo.c /usr/src/bar.cc $ echo $mypath:gt foo.c bar.cc | |||
| supprime l'extension en fin de variable. |
$ echo $mypath:r /usr/local/src/foo /usr/src/bar.cc $ echo $mypath:gr /usr/local/src/foo /usr/src/bar | |||
| donne l'extension correspondant à la fin de variable. |
$ echo $mypath:e c /usr/src/bar.cc $ echo $mypath:ge c cc | |||
| quote la variable pour éviter tout mécanisme de substitution. | ||||
|
|
Mécanismes de substitution.
Tableau 9. Clé de substitution
| Clé | Sémantique |
|---|---|
$?<name> = ${?<name>} | Renvoit 1 si la variable est définie, 0 sinon. |
$?0 | Renvoit 1 si $0 est définie, 0 sinon. |
$$ | Substituée par le pid du shell parent. |
$! | Substituée par le pid du dernier processus asynchrone lancé par le shell parent. |
$< | Substituée par une ligne de l'entrée standard. |
$ set v = toto
$ echo ${?v} # renvoit 1 puisque la variable v est définie
1
$ echo $u
u: Undefined variable.
$ echo ${?u} # moyen commode pour savoir si une variable est définie
0
Le shell définit pour chaque commande un certain nombre de paramètres « automatiques ».
Tableau 10. Opérateurs de comparaison
| Paramètre(s) | Utilisation |
|---|---|
$0 | nom de la commande ou du script invoqué. |
$1, $2 .. $n
${1}, ${2} .. ${n} | encore équivalent à $argv[1], $argv[2], .. $argv[n]
valeurs des paramètres positionnels passés à la commande ou au script. |
$* = $argv[*]
= ${argv[*]} | ensemble des paramètres, sauf $0. |
$# = $#argv
= ${#argv} | nombre de paramètres effectifs, $0 non compris. |
CODE : #!/bin/csh # ~/bin/testparm.sh echo 'je suis le script, $0 = ' $0 if ( ${#argv} > 0 ) then echo ' évoqué avec $# (= $#argv = ${#argv}) = ' ${#argv} ' arguments sur la ligne de commande' echo 'Voici les paramètres : $* (= $argv[*] = ${argv[*]}) = ' ${argv[*]} set i = 1 foreach param ( $argv[*] ) echo "paramètre ($i) : $param" @ i++ end else echo ' pas d arguments, nb arg $# (= $#argv = ${#argv}) = ' ${#argv} endif # fin du script EXEMPLE : $ ./testparam.sh toto titi tutu 147 je suis le script, $0 = ./testparam.sh évoqué avec $# (= $#argv = ${#argv}) = 4 arguments sur la ligne de commande Voici les paramètres : $* (= $argv[*]) = toto titi tutu 147 paramètre (1) : toto paramètre (2) : titi paramètre (3) : tutu paramètre (4) : 147 $ $ ./testparam.sh je suis le script, $0 = ./testparam.sh pas d arguments, nb arg $# (= $#argv = ${#argv}) = 0 $
Elle se fait en utilisant la variable spéciale $<
CODE : #!/bin/csh # ~/bin/testread.sh echo -n " Votre réponse (o, n) ? " set rep = $< if ( $rep == o ) then echo "réponse oui" else echo autre réponse : $rep endif EXEMPLE : $ ./testread.sh Votre réponse (o, n) ? yes autre réponse : yes $ ./testread.sh Votre réponse (o, n) ? o réponse oui
Règles :
Les expressions utilisent les mêmes opérateurs que le langage C (sémantique identique. On retrouve les instructions if, while, @ et exit,
Les chaines numériques commençant par un 0 sont interprétées comme des valeurs en base octale,
Le résultat de toute expression est une chaîne représentant un nombre décimal.
La valeur 0 s'interprète en contexte logique comme la valeur « faux », toutes les autres ont la valeur « vrai ».
Tableau 11. Opérateurs de comparaison
| Opérateur | Sémantique |
|---|---|
| O P E R A T E U R S D E F I C H I E R S | |
-d <filename> | est vraie si le fichier désigné par <filename> est un répertoire. |
-e <filename> | est vraie si le fichier désigné par <filename> existe. |
-f <filename> | est vraie si le fichier désigné par <filename> est un fichier régulier. |
-o <filename> | est vraie si le fichier désigné par <filename> est propriété de l'utilisateur. |
-r <filename> | est vraie si le fichier désigné par <filename> est un fichier accessible en lecture. |
-w <filename> | est vraie si le fichier désigné par <filename> est un fichier accessible en écriture. |
-x <filename> | est vraie si le fichier désigné par <filename> est un fichier exécutable. |
-z <filename> | est vraie si le fichier désigné par <filename> est de taille nulle. |
-b <filename> | [tcsh] est vraie si le fichier désigné par <filename> est un fichier spécial en mode bloc. |
-c <filename> | [tcsh] est vraie si le fichier désigné par <filename> est un fichier spécial en mode caractère. |
-l <filename> | [tcsh] est vraie si le fichier désigné par <filename> est un lien symbolique. |
-p <filename> | [tcsh] est vraie si le fichier désigné par <filename> est un tube nommés (FIFO). |
-S <filename> | [tcsh] est vraie si le fichier désigné par <filename> est un socket. |
| C O M P A R A I S O N D E C H A I N E S | |
<str1> == <str2> | est vraie si <str1> est égale à <str2>. |
<str1> != <str2> | est vraie si <str1> est différente de <str2>. |
<str1> =~ <str2> | est vraie si le motif <str2> (right hand side) correspond au motif <str1>. |
<str1> !~ <str2> | est vraie si le motif <str2> (right hand side) ne correspond pas au motif <str1>. |
| C O M P A R A I S O N D ' E N T I E R S | |
<int1> >= <int2> | est vraie si <int1> est supérieur ou égal à <int2>. |
<int1> > <int2> | est vraie si <int1> est strictement supérieur à <int2>. |
<int1> <= <int2> | est vraie si <int1> est inférieur ou égal à <int2>. |
<int1> < <int2> | est vraie si <int1> est strictement inférieur à <int2>. |
| C O M B I N A I S O N D ' E X P R E S S I O N S | |
| ( <expr> ) | est vraie si <expr> est vraie. |
! <expr> | est vraie si <expr> est fausse. |
<expr1> && <expr2> | est vraie si <expr1> et <expr2> sont simultanément vraies. |
<expr1> || <expr2> | est vraie si <expr1> ou <expr2> est/sont vraie(s). |
Tableau 12. Opérateurs (par ordre décroissant de priorité)
| Opérateurs | Sémantique |
|---|---|
| ( ) | regroupement |
| ~ | complément bit à bit |
| ! | négation logique. |
| * / % | multiplication, division, modulo |
| + - | addition, soustraction |
| << >> | décalage à gauche, décalage à droite |
| <= < >= > | opérateurs relationnels |
| == != =~ !~ | opérateurs de comparaisons de chaînes |
| & | « et » bit à bit |
| ^ | « ou exclusif » bit à bit |
| | | « ou » (inclusif) bit à bit |
| && | « et » logique |
| || | « ou » logique |
La commande interne @ permet de réaliser des opérations arithmétiques sur des valeurs ou variables entières, attention à respecter l'espace entre le symbole @ et la variable.
$ set v = 3 $ set u = 8 $ set p = 5 $ @ x = $v + $u * $p # attention à la priorité des opérateurs $ echo $x 43 $ @ x = ( $v + $u ) * $p 55 $ @ q = $u / $p # division entière $ @ r = $u % $p $ echo quotient = $q et reste = $r quotient = 1 et reste = 3 $ @ v++ # incrémentation d'une unité $ echo $v 4 $ @ v += 2 # incrémentation de deux unités $ echo $v 6 $ @ u /= 2 # @ u = u / 2 $ echo $u 4 $ @ u = ( $u << 3 ) # décalage à gauche de 3 bits, ce qui revient à une x 8 $ echo $u 32 $ @ v = (( $v + 4 ) >> 2 ) # 10 décaler de 2 bits à droite, soit 10 / 4 = 2 $ echo $v 2