2.6 L'interprète de commandes sh
2.6.1 Fichiers de commandes
sh est l'interprète de commandes.
Il peut se mettre en oeuvre lui-même.
Ainsi
sh <essai.sh >resultat
correspond au schéma global
Les commandes présentes dans le fichier
essai.sh (l'extension
.sh signifie que le fichier contient des commandes destinées à
sh) seront exécutées et leur résultat sera placé dans le fichier
resultat.
Une autre forme existe pour exécuter un fichier de commandes :
sh fichier
La différence réside dans les valeurs de la commande intrinsèque
read qui lit une ligne dans le flux entrant (essai.sh dans le premier cas et
/dev/tty dans le second).
Il est tellement usuel de possèder des fichiers de commandes que l'on peut
en demander l'exécution en nommant seulement le fichier à condition
de l'avoir rendu exécutable (cf.
chmod).
Ainsi
fichier arguments
conduit-il à
De fait puisqu'un programme exécutable est aussi archivé comme un fichier,
l'interprète de commandes lorsqu'il cherche à évaluer une commande comme
fichier arguments...
regarde tout d'abord si le fichier possède l'attribut exécutable puis
s'il correspond à des instructions binaires ou à un texte.
Suivant le cas il lance l'exécution du programme ou bien celle d'une tâche
basée sur
sh lisant ce fichier considéré comme une suite de commandes.
Cette tâche se terminera lorsque le fichier sera épuisé.
L'interprète de commandes (sh) offre des primitives de programmation
structurée :
La fin de ligne est considérée comme une fin de commandes ainsi que le
point-virgule.
On pourrait aussi écrire comme suit les exemples précédents :
if f77 essai.f ; then echo fini ; else echo erreur ; fi
et
{ ls -Ral
echo fini
}
La conditionnelle, ainsi que les primitives
while et
until,
ne se peuvent concevoir que parce que chaque tâche qui s'achève
retourne à son initiatrice un code:
-
zéro, si tout s'est bien passé,
-
une valeur non nulle, dans tous les autres cas.
Attention, sous sh les
valeurs de vérité sont représentées à l'inverse des conventions
usuelles. Pour C, Vrai est 1 et Faux, 0. Pour Pascal, Faux est avant
Vrai et usuellement représenté aussi par 0.
La commande
exit permet d'achever une suite de commandes en imposant le code de retour.
Par défaut ce code est nul.
Par exemple
if f77 essai.f
then exit 1
echo fini
else
echo "c'est bien erroné"
exit # comme exit 0 !
fi
Le retour sera Faux si la compilation s'est bien passé (et le message
fini ne sera pas produit).
Dans le cas contraire c'est le message
c'est bien erroné qui sera engendré et la valeur logique de retour sera Vrai !
2.6.2 Autres possibilités
De nombreuses autres possibilités existent, qui font de l'interprète de
commandes un véritable interprète de langage : Le langage
shell!
La notion de variable existe.
Ainsi :
prg=/u/untel/f77
déclare une variable nommée
prg ayant pour valeur la chaine de caractères
/u/untel/f77.
On peut utiliser cette variable en la préfixant du symbole
$.
f77 $prg/e.f
est équivalent à
f77 /u/untel/f77/e.f
C'est une simple substitution textuelle qui ignore la structure même des
commandes.
Ainsi
$ cde1='cat /'
$ cde2='o "Bonjour"'
$ ech$cde2
Bonjour
$ ${cde1}u/untel/f77/test.$cde2
listera le fichier
/u/untel/f77/test.o et le fichier
Bonjour.
Certaines variables prédéfinies existent comme
$0,$1, $2...$9, $PATH,
$USER ...
Les dix premières permettent de nommer l'argument de rang correspondant
lors de l'appel à une commande.
Ainsi peut-on faire des « sous-programmes » pouvant prendre facilement
jusqu'à neuf arguments ($0 est le nom du sous-programme appelé).
On peut mettre en un fichier nommé
compil le
programme (qu'UNIX nomme script!)
suivant :
if f77 $1
then exit
else { print $1 & echo $1 est erronné
exit 1
}
fi
puis appeler ce fichier par la commande
compil e.f
$1 sera connue comme étant la chaine de caractères
e.f durant tout le déroulement de la commande
compil.
$0 sera
compil,
les autres
$i seront vides.
compil compilera
e.f et en cas d'erreur
-
lancera, en parallèle, l'impression du fichier erroné
sur
/dev/lp,
-
produira un message explicatif,
-
et, enfin, retournera le code d'erreur 1 (Faux) qui pourra être employé
ultérieurement.
$PATH est l'ensemble des chemins d'accès où chercher les fichiers exécutables
pour un utilisateur (qui a son nom contenu dans la variable
$USER).
Par exemple, lors de la soumission d'une commande
fichier arguments
sh recherchera
fichier dans tous les répertoires mentionnés dans
$PATH et ceux-là seulement.
Si
$PATH est vide, vous ne pourrez vraisemblablement pas faire grand chose!
Usuellement
$PATH contient
:/usr/local:/bin:/usr/bin:/usr/ucb
Les répertoires sont séparés par
:.
Un répertoire vide désigne le répertoire courant.
Ici, une commande mentionnant
fichier est recherchée
-
dans le répertoire courant,
-
à défaut, dans le répertoire
/usr/local,
puis
/bin,
etc ...
On peut bien sûr étendre
$PATH en écrivant par exemple
PATH=$PATH:/u/queinnec/cmds
(cmds est le répertoire où je regroupe mes commandes).
$MAIL contient le nom du fichier boîte à lettres que
l'interprète scrutera toutes les cinq minutes afin de vous prévenir de
l'éventuelle arrivée de messages.
Attention, une variable est locale à la tâche où elle a été définie.
Pour qu'elle soit connue des tâches filles qui viendraient à être
créées, elle doit être exportée par la commande
export variable, variable ...
En aucun cas vous ne pourrez modifier la valeur d'une variable d'une
tâche ancêtre.
Par exemple, pour que la modification de
PATH précédement faite soit
connue de toute fille qui viendrait à être engendrée, il faudra
exporter cette variable, et pour cela écrire :
export PATH
Un autre mécanisme bien utile est que le
sh de connexion exécute systématiquement, s'il le trouve
dans le répertoire initial (valeur de
HOME),le fichier de commandes
.profile.
.profile peut donc contenir toutes les initialisations
personnelles que vous pourriez souhaiter. À titre d'exemple, voici mon
.profile personnel [Celui de 1985!]
#
# variables diverses
#
umask 022 # chmod go=r u=rw
PATH=":$HOME/cmds:/usr/local:/bin:/usr/bin:/usr/ucb:/etc"
# diverses options
SITE='Ensta' # nom du site
SPEED=300 # Bauds
ED=emin # Le préféré sur ce site
MORE='-cf'
# Les invites
if test $TERM = FT
then PS1='\\0315\\031`<' # dessine un MU suivi d'un OMEGA
else PS1="$SITE<- "
fi
PS2='Suite <- '
# message de bienvenue
echo "B I E N V E N U E sur $SITE"
#
trap ". $HOME/.eliforp" 0 # .logout en quelque sorte !
#
stty lnext "undef" nxpage 'undef' nxhalf 'u'
stty nxline 'u' flush 'u' erase '^H' kill '^U'
stty werase '^W' rprnt '^R' intr '^?' quit '^\\'
stty eof '^D' -pageen -pageon ctlecho
# courrier automatique
MAIL=/usr/spool/mail/$USER
if test -s $MAIL
then echo "$PS1 mail"
mail
fi
# les exportations
export MORE ED MAIL SITE SPEED TERM PATH PS1 PS2 USER SHELL
2.6.3 Expansion
Le mécanisme d'expansion est une des plus heureuses caractéristiques de
sh.
L'expansion permet de nommer simplement des groupes de fichiers.
Elle se fonde principalement sur l'emploi de deux caractères :
? et
*.
-
? remplace n'importe quel caractère,
-
* remplace n'importe quelle séquence de caractères.
Si le répertoire local (cf. le nom local) contient les fichiers :
a ab acb aard att itt
t t1 t2 t3 t6 t7
alors
-
? équivaut aux fichiers dont le nom ne se compose que d'un unique caractère,
soit
a et
t.
-
???? désigne les fichiers dont le nom comporte quatre lettres
exactement, c'est donc le seul fichier
aard.
-
t* regroupe tous les fichiers dont le nom débute par la lettre
t,
c'est-à-dire
t,
t1,
t2,
t3,
t6,
t7.
-
*t regroupe symétriquement les fichiers dont le nom se termine par la lettre
t,
c'est-à-dire :
t,
att,
et
itt.
-
D'autres combinaisons sont encore possibles comme
*t* qui représente les fichiers contenant la lettre
t.
On peut facilement voir le résultat d'une expansion grâce à
echo
echo *t*
provoque
att itt t t1 t2 t3 t6 t7
D'autres expanseurs plus spécifiques existent comme
t*[2-5]
qui sélectionne les fichiers dont le nom commence par
t et qui se terminent par un chiffre compris entre
2 et
5.
Soient pour l'exemple précédent,
t2 et
t3
Cette expansion est faite de façon intelligente suivant les répertoires
concernés.
Ainsi
echo ../pascal/t*
expansera la spécification
t* dans le répertoire
pascal,
frère du répertoire courant.
2.6.4 Déroulement
Lors de l'analyse d'une commande, la substitution de variables est effectuée
en premier puis viennent les expansions mais, attention, le premier terme
de la commande ne doit pas subir d'expansion.
Ce premier mot est soit une commande intrinsèque soit un nom de fichier,
Les mots suivants, c'est-à-dire des séquences de lettres séparées par des
blancs ou des tabulations ou sauts à la ligne (ou, plus exactement, les
caractères figurant dans la valeur de la variable
IFS) sont alors ses
arguments.
Les répertoires figurant dans
PATH sont alors scrutés séquentiellement
afin d'y rechercher un fichier dont le nom est celui du premier mot et qui
sera, si trouvé, lancé.
Tout ce qui précède n'est qu'une présentation succincte de l'interprète
de commandes
sh.
D'autres existent, plus élaborés, qui ont pour nom
csh,
nsh,
vsh,
tcsh,
cwsh...Tout interprète a ses détracteurs comme ses prosélytes !
Mais dans tous les cas ce ne sont que des programmes au dessus du coeur d'UNIX
et tous peuvent s'exécuter en parallèle pour le bonheur de chacun.
La variable
SHELL vous renseignera sur votre interprète de commandes courant.
Vous êtes vivement invité à méditer les quelques sept pages de la
documentation standard UNIX décrivant
sh.
ainsi qu'à étudier les fichiers de commandes qui sont accessibles (par
exemple sous
/usr/local).