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

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.
	if test -f toto
		then echo "-----------------------------------------------" >>toto
		else echo "Etats des resultats successif de test" > toto
	fi
	date >>toto
	echo "-----------------------------------------------" >>toto
	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.