3. Structures de Contrôle & autres éléments algorithmiques

Ces structures algorithmiques sont utilisées dans les scripts shells. Pour rédiger un script shell (ici, en tcsh), il faut :

  1. Créer le fichier avec un éditeur de texte (sample.sh). La première ligne doit commencer avec le prologue [shebang] : #!/bin/tcsh.

  2. Lui attribuer la permission d'exécution sur le fichier créé (chmod +x sample.sh).

  3. 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).

3.1. Alternatives

3.1.1.  La structure alternative if

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 !


3.1.2.  La structure alternative switch

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]


3.2. Boucles

3.2.1.  Boucle while

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


3.2.2. Boucle repeat

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 !


3.2.3.  Boucle foreach

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                                                          
$


3.3. Variables utilisateurs

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émantiqueExemple
:h   :gh
interprète le contenu de la variable comme un nom de fichier Unixet en donne le début, i.e. la partie avant le dernier /

$ echo $mypath:h                    
/usr/local/src /usr/src/bar.cc
$ echo $mypath:gh
/usr/local/src /usr/src

:t   :gt
interprète le contenu de la variable comme un nom de fichier Unix™ et en donne la fin, i.e. la partie après le dernier /

$ echo $mypath:t                    
foo.c /usr/src/bar.cc
$ echo $mypath:gt
foo.c bar.cc

:r   :gr
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

:e   :ge
donne l'extension correspondant à la fin de variable.

$ echo $mypath:e                    
c /usr/src/bar.cc
$ echo $mypath:ge
c cc

:q   :gq
quote la variable pour éviter tout mécanisme de substitution. 
:x   :gx
idem précédent, mais cassure en mots sur les caractères : blanc, espace et nouvelle ligne.
 


Mécanismes de substitution. 

Tableau 9. Clé de substitution

CléSémantique
$?<name> = ${?<name>}Renvoit 1 si la variable est définie, 0 sinon.
$?0Renvoit 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 

3.4. Paramètres du shell

Le shell définit pour chaque commande un certain nombre de paramètres « automatiques ».

Tableau 10. Opérateurs de comparaison

Paramètre(s)Utilisation
$0nom 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
$

3.5. Lecture sur l'entrée standard

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

3.6. Expressions conditionnelles

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érateurSé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érateursSé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


Skins :
Transparence
Simple
Page Accueil
Formation