15
Le langage de commandes
Le langage de commandes est à la base de la
communication entre un utilisateur et le système. Son rôle
essentiel est de permettre à un tel utilisateur de lancer
l'exécution de programmes. Nous avons vu qu'un programme
s'exécutait dans un certain environnement, et pouvait manipuler des
objets externes. Le langage de commande doit donc aussi permettre de
définir cet environnement et les liaisons avec ces objets externes. Le
besoin de contrôler les différentes opérations faites par
ces programmes entraîne la nécessité de pouvoir identifier
l'utilisateur qui lance les commandes.
15.1. Le langage issu du traitement par lot
La forme syntaxique de ce langage varie suivant les
constructeurs et le mode d'utilisation de la machine. À l'origine, ce
mode étant uniquement le traitement par lot à partir de cartes
perforées, le langage présentait une forme très
rudimentaire. Même si les cartes ne sont plus utilisées de nos
jours, le langage pour ce mode en est resté fortement
imprégné. Les lignes contenant une phrase du langage de commandes
commencent par un (ou plusieurs) caractère spécial, comme '$' ou
'//'. Elles définissent la suite des étapes (step)
d'un travail (job), chaque étape correspondant à
l'exécution d'un programme particulier. La définition d'une
étape consiste donc à définir le programme à
exécuter, l'environnement d'exécution de ce programme, et les
liaisons avec les objets externes. Les communications d'une étape
à la suivante sont souvent inexistantes. Les utilisateurs doivent
prévoir ces communications par le biais de fichiers. Ce langage comporte
souvent un nombre réduit de commandes, chacune de ces commandes
comportant par contre un nombre important de paramètres. De fait, trois
commandes sont essentielles:
- La commande d'identification du travail a pour
but de préciser l'identité du demandeur, et les ressources
globales nécessaires.
- La commande de
définition d'une étape définit le programme à
exécuter et les paramètres de son
environnement.
- La commande de définition
d'une liaison permet de relier un flot particulier du programme de
l'étape à un objet externe.
Le grand nombre
de paramètres d'une commande condamne, en général, la
définition des paramètres par position, comme dans les langages de
programmation habituels. Elle se fait plutôt par le biais de mots
clés, sous la forme d'une liste d'associations, où une association
est un mot clé suivi de la valeur du paramètre correspondant.
Chaque valeur d'un paramètre étant associée à un mot
clé, l'ordre des paramètres n'a plus d'importance. Par ailleurs,
l'interpréteur du langage peut donner des valeurs par défaut aux
paramètres que la commande ne définit pas. En
général, ces valeurs par défaut sont choisies par
l'ingénieur système de l'installation de façon à
correspondre aux valeurs les plus couramment demandées par les
utilisateurs.
Dans certains langages, il est possible de définir des
macrocommandes. Une macrocommande est désignée par un nom
et représente une suite de commandes prédéfinies. Elle peut
être paramétrée, ce qui permet de lui donner une certaine
généralité. Lorsqu'elle est appelée, la suite de
commande qu'elle représente est d'abord modifiée en fonction de
ces paramètres, puis exécutée. Ce mécanisme permet
de simplifier l'utilisation du langage de commande lorsqu'une même suite
de commandes est fréquemment utilisée.
15.2. Le langage interactif
En mode interactif, l'utilisateur doit d'abord fournir au
système son identité pour établir la connexion
(login). Le système lance ensuite l'interpréteur de
commandes qui va permettre à l'utilisateur de faire exécuter les
commandes qu'il désire durant une période plus ou moins longue,
que l'on appelle une session, et qui se terminera par une
déconnexion (commande logout). La durée de cette session,
et le fait de la présence de l'utilisateur au terminal implique une forme
différente du langage de commandes.
15.2.1. Le message d'invite
Alors que, en traitement par lot, l'utilisateur doit
définir l'ensemble des commandes et des données dans un fichier de
travail avant de le soumettre au système, en mode interactif, il doit
pouvoir prendre ses décisions au vu des résultats de chaque
commande successive. En conséquence, il ne doit pas être contraint
de fournir des informations à l'avance, mais uniquement lorsqu'elles sont
effectivement nécessaires. La notion de fichier de travail a donc moins
de sens. Il n'est plus nécessaire de distinguer une ligne de commande
d'une ligne contenant des données par des caractères
spéciaux comme précédemment, mais d'indiquer à
l'utilisateur l'outil logiciel qui attend des informations de sa part. C'est ce
que l'on appelle l'invite (en anglais le prompt), qui est
constituée d'une chaîne de caractères qui est envoyée
à l'utilisateur par l'outil lorsqu'il attend ces informations. Par
exemple, l'interpréteur de commandes de VMS, comme celui d'Unix, envoie
normalement un '$' en début de ligne lorsqu'il attend une nouvelle
commande. Souvent l'invite est une chaîne paramétrable, permettant
ainsi à l'utilisateur de personnaliser ce dialogue.
15.2.2. Un langage orienté verbe
La deuxième différence, avec le traitement par
lot, est le besoin de plus de souplesse et de convivialité du langage
lui-même. Il s'ensuit que, au lieu d'avoir peu de commandes avec un grand
nombre de paramètres, l'orientation est plutôt vers un grand nombre
de commandes ayant chacune peu de paramètres. On dit parfois que l'on a
une syntaxe orientée verbe, car la commande est constituée
d'un verbe, qui est le nom de la commande, suivi des paramètres
éventuels de la commande. Certaines de ces commandes sont reconnues et
exécutées directement par l'interpréteur. Les autres
commandes sont considérées comme des demandes d'exécution
d'un programme. L'interpréteur recherche alors le programme
exécutable de ce nom, en demande le chargement et le lancement au
système, en lui fournissant les paramètres de la commande. Ceci
donne une caractéristique importante au langage de commandes qui est son
extensibilité, puisque au fur et à mesure où de nouveaux
programmes deviennent opérationnels, ils deviennent de nouvelles
commandes du système.
15.2.3. La gestion de l'environnement
La troisième caractéristique du langage de
commandes interactif concerne la gestion de l'environnement de l'utilisateur.
Alors que, en traitement par lot, une session de travail consiste en un nombre
réduit d'étapes qui s'effectuent chacune dans un environnement
propre et souvent indépendant, en mode interactif, une session peut durer
plusieurs heures, et donc être constituée d'un grand nombre de
commandes. Il serait fastidieux pour l'utilisateur de devoir préciser
complètement l'environnement de chacune de ces commandes. On peut
définir l'environnement comme un ensemble d'informations propres
à l'utilisateur dans sa session courante, qui sont gérées
par l'interpréteur de commandes, qu'il utilise pour modifier les
commandes et leurs paramètres préalablement à leur
exécution, et qu'il peut transmettre en totalité ou en partie aux
programmes correspondants. Nous avons déjà eu un aperçu de
telles informations:
- Le message d'invite est en général
défini par une variable de l'environnement. La personnalisation de ce
message est obtenue par l'utilisateur en modifiant cette
variable.
- Le répertoire de travail, dont
nous avons parlé dans le chapitre sur la désignation des objets
externes, fait partie de cet environnement. Il peut être changé
à tout moment, et est communiqué aux programmes (commande
cd de beaucoup de
systèmes).
- Les règles de recherche,
dont nous avons également parlé dans le chapitre 10, font aussi
partie de l'environnement. En particulier, une variable de l'environnement
définit par exemple la liste des noms absolus des répertoires dans
lesquels l'interpréteur recherche les programmes exécutables
correspondant aux noms des commandes (variable
PATH d'Unix). L'utilisateur peut
également définir d'autres variables de ce type, qui peuvent
être transmises aux programmes pour leurs propres besoins de recherche.
Par exemple un programme d'aide en ligne (help dans Multics ou VMS,
man dans Unix) peut utiliser une telle variable pour localiser les
fichiers de documentation des applications manipulées par
l'utilisateur.
- Le type de terminal,
utilisé dans la connexion, est souvent défini par une variable de
l'environnement (variable TERM d'Unix).
Elle peut être consultée par la méthode d'accès
terminal virtuel ou par les programmes spécifiques (éditeurs
pleine page).
15.2.4. Le traitement préliminaire de la commande
Pour simplifier la frappe des commandes par l'utilisateur, les
interpréteurs de commandes effectuent souvent différents
traitements sur la commande avant son exécution proprement
dite.
Le mécanisme de substitution permet le
remplacement dans la commande des variables de l'environnement par leur valeur
courante. Par exemple, dans Unix, la variable
HOME de l'environnement est
initialisée par le nom absolu du répertoire courant de
l'utilisateur lors de l'établissement de la connexion.
${HOME} dans une commande sera
remplacée par ce nom. Il peut également être utilisé
pour construire la liste des noms de fichiers vérifiant un certain
profil. Par exemple, '*.c' dans une
commande sera remplacé par la liste des noms de fichiers du
répertoire courant qui sont suffixés par
'.c'.
Le mécanisme d'abréviation permet
à l'utilisateur de donner une forme plus simple à certaines
commandes ou suite de commandes dont il se sert souvent, en fixant certains
paramètres et en permettant éventuellement une certaine forme de
substitution. Ce mécanisme s'apparente à la macrosubstitution. Par
exemple, l'abréviation suivante:
alias rm 'rm -i'
est
très conseillée sur Unix ou ses dérivés. Elle change
l'option par défaut de la commande
rm de destruction d'un fichier, de
façon à demander systématiquement la confirmation à
l'utilisateur.
Considérons, dans un deuxième exemple, la
définition de l'abréviation suivante:
alias save 'chmod u+w \!:1.sav; cp \!:1.c \!:1.sav; chmod u-w \!:1.sav'
Lors
de la frappe de la commande save truc, le
système constate l'utilisation de l'abréviation de nom
save, avec le paramètre
truc. Il substitue partout dans
l'abréviation la chaîne \!:1,
qui désigne le premier paramètre, par
truc et obtient la suite de commandes
suivante dont il lance l'exécution. Cette suite consiste à forcer
d'abord l'autorisation d'écriture par le propriétaire du fichier
truc.sav puis à copier le fichier
truc.c dans
truc.sav et à enlever l'autorisation
d'écriture par le propriétaire sur ce fichier.
chmod u+w truc.sav; cp truc.c truc.sav; chmod u-w truc.sav
L'utilisateur
se protège ainsi d'une modification involontaire de ses fichiers
*.sav.
15.2.5. L'exécution de la commande
Nous avons déjà dit que l'exécution d'une
commande, non reconnue par l'interpréteur, consistait à rechercher
le fichier de même nom par les règles de recherche, à
demander au système de charger le programme correspondant, à lui
transmettre les paramètres et l'environnement, et enfin à lancer
son exécution. Dans certains systèmes il y a de plus
création d'un processus pour cette exécution. La plupart du temps,
les paramètres sont les chaînes de caractères
présentes dans la ligne de commande, et dont l'interprétation est
laissée au programme. L'environnement peut comprendre évidemment
certaines variables dont nous avons déjà parlé, mais il
doit définir l'ensemble du contexte d'exécution du programme. En
particulier, le programme doit être capable d'accéder aux objets
externes dont il a besoin. Si la définition des liaisons correspondantes
ne font pas partie du programme, elles doivent lui être transmises par
l'intermédiaire de l'environnement. La plupart des systèmes
considèrent que l'environnement doit définir au moins trois
liaisons:
- l'entrée standard où le programme lira
ses données,
- la sortie standard où le programme écrira
ses résultats,
- la sortie des erreurs où le programme
écrira ses messages d'erreur.
En mode interactif, il est assez naturel que les trois
liaisons correspondent au terminal de l'utilisateur. Cependant, il peut arriver
que l'utilisateur désire que ce soit autrement. Par exemple, il
désire que le programme lise ses données dans un fichier, ou
écrive ses résultats dans un fichier. Le langage de commandes doit
alors fournir un mécanisme de redirection. Dans Unix ou MS-DOS et
leurs dérivés, ceci est obtenu non pas par une définition
des liaisons dans l'environnement, mais en établissant ces trois liaisons
préalablement au lancement du processus. De plus, comme Unix crée
un processus par commande, l'interpréteur du langage de commandes permet
la redirection de la sortie standard d'un processus vers un tube, et la
redirection de l'entrée standard d'un autre processus depuis ce tube. Par
exemple, la ligne de commandes
cat *.biblio | sort | uniq > biblio.tri
lance
trois processus sur chacune des trois commandes
cat,
sort et
uniq, la sortie du premier processus, qui
est la concaténation des fichiers suffixés
.biblio du répertoire courant,
étant délivrée en entrée du deuxième, qui
trie en ordre alphabétique les lignes de cette concaténation pour
les envoyer au troisième, qui élimine les
répétitions et envoie le résultat dans le fichier
biblio.tri. Comme les sorties d'erreurs de
ces processus ne sont pas redirigées, leurs éventuels messages
d'erreurs seront envoyés au terminal.
Signalons enfin que bien souvent les programmes peuvent
transmettre un code de retour à l'interpréteur de commande
lorsqu'ils se terminent. Il est donc possible de tenir compte de ces codes de
retour pour la poursuite de l'exécution d'une séquence de
commandes. Ceci conduit à disposer, dans le langage de commandes, de
structures de contrôle proches de celles des langages de
programmation.
15.3. Les structures de contrôle du langage de commandes
Certains systèmes offrent un langage de commandes qui
est un véritable langage de programmation, en ce sens qu'il
possède des structures de contrôle complexes. Il n'est pas question
évidemment de remplacer les langages de programmation classiques, mais de
fournir des structures de contrôle qui soient appropriées aux
objets manipulés par le langage de commandes, c'est-à-dire,
essentiellement les fichiers et les processus. Nous mentionnerons deux
structures de ce types et montrerons leur utilisation sur des
exemples.
- La commande conditionnelle permet, en
fonction du code de retour d'un programme d'exécuter l'une ou l'autre
parmi deux suites de commandes. Par exemple, nous utilisons la commande
test -f toto qui permet de savoir si le
fichier toto existe comme fichier ordinaire
dans le répertoire courant. S'il existe, on trace une ligne de tirets de
séparation après ce qui y est déjà, et sinon on y
écrit une première ligne d'indication. On y écrit ensuite
la date courante suivie d'une nouvelle ligne de tirets. Noter l'utilisation de
> pour une redirection et
écriture en début de fichier, et
>> pour une redirection en
prolongement d'un fichier déjà existant sans perte de son
contenu.
if test -f toto
then echo "-----------------------------------------------" >>toto
else echo "Etats des resultats successif de test" > toto
fi
date >>toto
echo "-----------------------------------------------" >>toto
- L'itération bornée permet
d'exécuter un ensemble de commandes, sur une suite de valeurs prises dans
une liste. Par exemple:
for i in toto truc bidule; do cp /usr/moi/$i /tous; done
exécute
la commande cp pour toutes les valeurs
possibles de la liste toto truc bidule. Ces
trois fichiers sont donc recopiés depuis le répertoire
/usr/moi dans le répertoire
/tous. Évidemment la liste peut
être construite automatiquement par le mécanisme de substitution vu
précédemment. Ainsi, pour faire la compilation de tous les
fichiers contenant des modules sources en langage
C du répertoire courant, il suffit
d'exécuter:
for i in *.c; do cc -c $i; done
Il
n'est pas question de développer ici un langage de commandes particulier,
c'est pourquoi nous ne décrirons pas plus ces structures de
contrôle. Il nous paraît plus important que le lecteur comprenne
leur importance, et le gain qu'il peut en obtenir. La complexité des
commandes que l'on peut avoir, conduit naturellement à la notion de
programme de commandes, mémorisé dans un fichier, et
exécutable en tant que tel, ce qui est un autre aspect de
l'extensibilité du langage de commandes dont nous avons
déjà parlé. Il nous semble avoir, dans ces deux exemples
ci-dessus, montré la différence entre le langage de commandes et
un langage de programmation. Le premier manipule surtout des fichiers sur
lesquels il exécute des commandes, le second manipule des données
sur lesquels il effectue des calculs.
15.4. Le langage à base de menus ou d'icônes
La convivialité du poste de travail est souvent
grandement amélioré par l'utilisation d'un langage de commandes
basé soit sur des menus déroulants soit sur des icônes, soit
sur les deux. Par exemple, sur MacIntosh ou sur Atari, la copie de fichier peut
être obtenue en déplaçant l'icône du fichier d'une
fenêtre dans une autre au moyen de la souris. De même sous X-window,
l'utilisateur peut construire ses propres menus déroulants pour ses
commandes les plus courantes. Pour le moment, ce type de langage fonctionne
très bien pour un nombre réduit de commandes souvent sans
paramètres. Il est agréable et doit être
systématiquement développé et utilisé pour les
besoins courants, mais il ne peut probablement pas satisfaire l'ensemble des
besoins des utilisateurs.
15.5. Conclusion
+ Le langage issu du
traitement par lot comporte peu de commandes, avec beaucoup de
paramètres. Les commandes permettent l'identification du travail, sa
décomposition en étapes et les définitions des liaisons
avec les objets externes. Les paramètres sont définis par mots
clés, avec des valeurs par défaut.
+ En interactif, les
outils logiciels se présentent aux utilisateurs par leur message
d'invite.
+ Le langage de
commandes interactif est plus convivial, avec une syntaxe orientée verbe,
définissant de nombreuses commandes ayant chacune peu de
paramètres. Chaque commande étant en fait un programme, le langage
est facilement extensible, par adjonction de nouveaux programmes.
+ L'environnement est
un ensemble d'informations propres à l'utilisateur dans sa session
courante, qui peuvent être utilisées par l'interpréteur de
commandes et les programmes pour adapter leur comportement aux besoins
spécifiques actuels de l'usager (message d'invite, répertoire de
travail, règles de recherche, type de terminal).
+ Les commandes de
l'utilisateur peuvent être simplifiées, l'interpréteur
pouvant les développer par les mécanismes de substitution ou
d'abréviation.
+ L'environnement
d'exécution d'un programme comporte la définition des liaisons
avec les objets externes. Au moins trois liaisons doivent être
définies pour l'entrée, la sortie et les erreurs. Si, en
général, ces liaisons sont avec le terminal de l'utilisateur, il
faut pouvoir les rediriger vers des fichiers.
+ Les programmes
peuvent transmettre, en fin d'exécution, un code de retour à
l'interpréteur de commandes, qui peut en tenir compte lors de
l'exécution d'un ensemble de commandes.
+ Les langages de
commandes modernes disposent de toutes les structures de contrôle des
langages de programmation classiques. La différence essentielle
réside dans les objets manipulés par ces deux types de
langage.
+ Le langage à
base de menus déroulants ou d'icônes offre une convivialité
remarquable pour les besoins les plus courants. Il ne peut couvrir l'ensemble
des besoins, où des structures de contrôle élaborées
sont nécessaires.