diff --git a/00TODO b/00TODO index 512bf52..3565805 100644 --- a/00TODO +++ b/00TODO @@ -90,7 +90,6 @@ cours/unix/imprimer.html cours/unix/index.html cours/unix/jeux.html cours/unix/processus.html -cours/unix/shell.html exos/contact.html exos/contact_sol.html exos/cp_mv.html diff --git a/unix/shell.tml b/unix/shell.tml new file mode 100644 index 0000000..2fc6ae6 --- /dev/null +++ b/unix/shell.tml @@ -0,0 +1,740 @@ + + + + +Shell + + + +

Le shell

+ +

Présentation

+ +

+Le machin qui interprète les commandes. +

+ + +

Comment le shell trouve-t-il les commandes ? +

+ +

+La variable PATH contient le chemin d'accès +aux commandes. Le shell l'utilise pour trouver les commandes. Il s'agit d'une +liste de répertoires séparés par des :. La plupart des commandes +sont en fait des programmes, c'est-à-dire des fichiers qu'on trouve dans le +système de fichiers. +

+ +

+Par exemple, quand vous tapez ls, le shell exécute le fichier +/bin/ls. Pour trouver ce fichier, il cherche dans le premier +répertoire du PATH un fichier qui s'appelle ls. +S'il ne trouve pas, il cherche ensuite dans le deuxième répertoire et ainsi +de suite. +

+ +

+S'il ne trouve la commande dans aucun répertoire du PATH, le shell +affiche un message d'erreur. Exemple: +

+ +
+chaland ~ $ sl
+sl: Command not found
+ + +

Commandes internes

+ +

+Certaines commandes du shell ne sont pas des programmes mais des commandes +internes (builtins functions). Elles sont directement +reconnues et exécutées par le shell. Un exemple de commande interne est +cd. C'est le répertoire courant du shell qui est modifié par +cd, ce qui signifie que le script suivant : +

+ +
+#! /bin/sh
+cd $*
+ +

+ne marche pas, car le shell lance un autre shell pour exécuter le script. +C'est ce sous-shell qui change son répertoire courant, et ce changement est +perdu quand le sous-shell meurt. +

+ +

Quels programmes utilisent le langage du shell ?

+ +

+Scripts pour automatiser ou systématiser des tâches, +opérations un peu compliquées. +

+ +

+sh : .profile (shell de login) ; sh lance un autre shell qui prend la relève +pour la session. +.xinitrc (pour lancer X) +

+ +

+Il existe un script spécial, qui est exécuté au moment où on se connecte. Ce +script est contenu dans le fichier $HOME/.profile. C'est ce +fichier qui vous dit s'il y a de nouveaux messages dans forum, si vous avez +du courrier, etc. +

+ +

+Ce fichier est normalement mis à jour automatiquement par les scripts de la +config conscrits. Il est néanmoins possible de le modifier pour changer des +options. +

+ +

La ligne de commande

+ +

Raccourcis pour les noms de fichiers

+ +

+Il est parfois ennuyeux d'avoir à taper un nom complet de fichier comme +nabuchodonosor. Il est encore plus ennuyeux d'avoir à taper une +liste de fichier pour les donner en arguments à une commande, comme : +

+ +
cc -o foo bar.c gee.c buz.c gog.c
+ +

+Pour éviter ces problèmes, on peut utiliser des jokers +(wildcards en anglais). +

+ +

L'étoile

+ +

+Une étoile * dans un nom de fichier est +interprétée par le shell comme « n'importe quelle séquence de +caractères » (mais ça ignore les fichiers dont le nom commence par un +point). Exemple: +

+ +
cc -o foo *.c
+ +

+Pour interpréter l'étoile, le shell va faire la liste de tous les noms de +fichiers du répertoire courant qui ne commencent pas par . et +qui finissent par .c. Ensuite, il remplace *.c +par cette liste (triée par ordre alphabétique) dans la ligne de commande, et +exécute le résultat, c'est-à-dire par exemple : +

+ +
cc -o foo bar.c buz.c foo.c gee.c gog.c
+ +

Le point d'interrogation

+ +

+On a aussi lepoint d'interrogation ?, qui +remplace un (et exactement un) caractère quelconque (sauf un point en début +de nom). Par exemple, ls *.? liste tous les dont l'extension +ne comporte qu'un caractère (.c, .h...). +

+ +

Les crochets

+ +

+La forme [abcd] remplace un caractère quelconque parmi +a, b, c, d. Enfin, +[^abcd] remplace un +caractère quelconque qui ne se trouve pas parmi a, b, +c, d. +

+ +

Exemple

+ +
echo /users/*
+ +

+affiche à peu près la même chose que +

+ +
ls /users
+ +

+(La commande echo se contente d'afficher ses arguments.) +

+ +

+&icone.attention; Attention : +

+ + + +

+Interlude: comment effacer un fichier nommé ?* ? On ne peut +pas taper rm ?* car le shell remplace ?* par la +liste de tous les fichiers du répertoire courant. On peut taper rm -i + * qui supprime tous les fichiers, mais en demandant confirmation à +chaque fichier. On répond n à toutes les questions sauf +rm: remove  ?*. +Autre méthode: utiliser les mécanismes de quotation. +

+ + +

Quotation

+ +

+Avec tous ces caractères spéciaux, comment faire pour passer des arguments +bizarres à une commande ? Par exemple, comment faire afficher un point +d'interrogation suivi d'une étoile et d'un dollar par echo ? +Le shell fournit des mécanismes pour ce faire. Ce sont les +quotations. +

+ +

Le backslash (\)

+ +

+ Il suffit de précéder un caractère spécial d'un backslash, et le shell +remplace ces deux caractères par le caractère spécial seul. Évidemment, le +backslash est lui-même un caractère spécial. +

+ +

+Exemples: +

+ +
+chaland ~ $ echo \?\*\$
+?*$
+chaland ~ $ echo \\\?\\\*\\\$
+\?\*\$
+ +

Les apostrophes ou simples quotes (')

+ +

+Un autre moyen est d'inclure une chaîne de caractères entre apostrophes +(simples quotes) '. Tout ce qui se trouve entre deux apostrophes +sera passé tel quel par le shell à la commande. +Exemple: +

+ +
+chaland ~ $ echo '$?*'
+$?*
+ +

Les guillemets doubles ou doubles quotes (")

+ +

+Les guillemets se comportent comme les apostrophes, à une exception près: les +dollars et les backslashes sont interprétés entre les guillemets. Exemple: +

+ +
+chaland ~ $ echo "$HOME/*"
+/users/87/maths/doligez/*
+ +

+Une technique utile: Quand on juxtapose deux chaînes de caractères quotées, le +shell les concatène, et elles ne forment qu'un argument. Exemple: +

+ +
+chaland ~ $ echo "'"'"'
+'"
+ +

+Quant aux interactions plus compliquées (backslashes à l'intérieur des +guillemets, guillemets à l'intérieur des apostrophes, etc.), le meilleur moyen +de savoir si ça donne bien le résultat attendu est d'essayer. La commande +echo est bien utile dans ce cas. +

+ +

Les backquotes (`)

+ +

+Dernière forme de quotation: `commande`. Le shell +exécute la commande indiquée entre backquotes, lit la sortie de la +commande mot par mot, et remplace ` commande +` par la liste de ces mots. Exemple : +

+ +
+chaland ~ $ echo `ls`
+Mail News bin foo lib misc mur notes.aux notes.dvi notes.log
+notes.tex planar text
+chaland ~ $ ls -l `which emacs`
+-rwxr-xr-t  1 root   taff   978944 Jul 16  1991 /usr/local/bin/emacs
+ +

+La commande which cmd employée ci-dessus affiche sur sa +sortie le nom absolu du fichier exécuté par le shell quand on lance la +commande it cmd : +

+ +
+chaland ~ $ which emacs
+/usr/local/bin/emacs
+ + +

Redirections

+ +

+Chaque commande a une entrée standard, une sortie standard, +et une sortie d'erreur. Par défaut, l'entrée standard est le clavier, +la sortie standard est l'écran, et la sortie d'erreur est aussi l'écran. +

+ +

Rediriger la sortie dans un fichier : >

+ +

+On peut rediriger la sortie standard d'une commande vers un fichier +(caractère >). Le résultat de la commande sera placé dans le +fichier au lieu de s'afficher sur l'écran. Exemple: +

+ +
chaland ~ $ ls -l > foo
+ +

+Le résultat de ls -l ne s'affiche pas à l'écran, mais il est placé +dans le fichier foo. On peut alors taper +

+ +
chaland ~ $ less foo
+ +

+(ou more foo) +pour lire le fichier page par page. +

+ +

Rediriger l'entrée : <

+ +

+On peut aussi rediriger l'entrée standard d'une commande (caractère +<). La commande lira alors le fichier au lieu du clavier. +Exemple: +

+ +
chaland ~ $ elm leroy < foo
+ +

+envoie par mail à Xavier Leroy le résultat de la commande ls -l de +tout à l'heure. +

+ +

+On peut aussi taper more < foo qui est équivalent à +more foo car more sans argument lit son entrée +standard et l'affiche page par page sur le terminal. +

+ +

Connecter la sortie d'une commande sur l'entrée d'une autre : +|

+ +

+On peut se passer du fichier intermédiaire grâce à un pipe +(caractère |). Un pipe connecte directement la sortie standard d'une +commande sur l'entrée standard d'une autre commande. Exemple: pour afficher +page par page la liste des fichiers du répertoire courant, faire +

+ +
chaland ~ $ ls -l | less
+ +

+Le pipe est d'un usage très courant et rend beaucoup de services. +

+ +

Récapitulatif

+ +

+La panoplie complète des redirections est la suivante (le tableau indique les +redirections qui diffèrent selon les shells) : +

+ + + + + + + + + + + + + + + + + + + + + + + + +
Fonction Bourne shell Z-shell
Redirige la sortie d'erreur (2) et la sortie standard (1) sur l'entrée +de la commande suivante2>&1 | |&
Redirige la sortie d'erreur et la sortie standard dans un fichier2>&1 > >&
Redirige la sortie d'erreur et la sortie +standard à la fin d'un fichier existant2>&1 >>>>&
+ +

Remarques

+ +

+Normalement, une redirection avec > sur un fichier qui +existe déjà efface le contenu du fichier avant d'y placer le résultat de la +commande. Les shells ont des options pour demander confirmation, ou +refuser d'effacer le fichier. +

+ +

+Une ligne de commandes contenant des | s'appelle un pipe-line. +Quelques commandes souvent utilisées dans les pipe-lines sont: +

+ + + +

Exemples

+ +
cat glop buz > toto
+ +

+Concatène les fichiers glop et buz et place le +résultat dans toto. +

+ +
wc -w /usr/dict/words
+ +

+Affiche le nombre de mots du dictionnaire Unix. +

+ +
grep gag /usr/dict/words | tail
+ +

+Affiche les 10 derniers mots du dictionnaire qui contiennent la chaîne +gag. +

+ + +

Variables

+ +

+Le shell a des variables. Pour désigner le contenu d'une variable, on écrit +le nom de la variable précédé d'un dollar. Exemple: echo $HOME +affiche le nom du répertoire personnel de l'utilisateur. +

+ +

+La façon de donner une valeur à une variable varie selon le type de shell +utilisé : +

+ +

+C-Shell (csh, tcsh, +lcsh) : on utilise la commande setenv : +

+ +
 
+chaland ~ $ setenv foo bar
+chaland ~ $ echo $foo
+bar
+ +

+Famille des Bourne Shell (sh, bash, +zsh, ksh) : on utilise export : +

+ +
+chaland ~ $ foo=bar
+chaland ~ $ export foo
+chaland ~ $ echo $foo
+bar
+ +

+Les valeurs des variables sont accessibles aux commandes lancées par le shell. +L'ensemble de ces valeurs constitue l'environnement. On peut aussi +supprimer une variable de l'environnement avec unsetenv +(C-Shell) ou unset (Bourne Shell). +

+ +

+Quelques variables d'environnement: +

+ + + +

+Exercice : Assurez-vous que /usr/local/games/bin se trouve +bien dans votre PATH. +

+ +

Programmation du shell sh

+ +

+Un shell, quel qu'il soit, peut exécuter des commandes prises dans un +fichier. Un fichier contenant des commandes pour le shell est appelé un +script. C'est en fait un programme écrit dans le +langage du shell. Ce langage comprend non seulement les commandes que +nous avons déjà vues, mais aussi des structures de contrôle +(constructions conditionnelles et boucles). +

+ +

+Pour la programmation du shell, nous allons utiliser le shell sh, +qui est le plus répandu et standard. Ce que nous avons vu jusqu'ici +s'applique aussi bien à sh qu'à zsh et aussi à +csh, à l'exception de setenv et de certaines +redirections signalées. Pour être un script, un fichier doit commencer par +la ligne: +

+ +
#!/bin/sh
+ +

+Il doit aussi avoir être exécutable (bit x). Le +#!/bin/sh sur la première ligne indique que ce script doit être +exécuté par le shell sh dont on indique le chemin d'accès. +

+ + +

Structures de contrôle

+ + +

for

+ +
+for var in liste de chaînes ;
+do commandes ; done
+ +

+Affecte successivement à la variable de nom var chaque chaîne de +caractères trouvée dans la liste de chaînes, et exécute les +commandes une fois pour chaque chaîne. +

+ +

+Rappel : $var accède à la valeur courante de +var. La partie commandes est une suite de commandes, +séparées par des ; ou des retours à la ligne. (Tous les +; dans cette syntaxe peuvent aussi être remplacés par des +retours à la ligne.) +

+ +

+Exemple : +

+ +
for i in *; do echo $i; done
+ +

+affiche les noms de tous les fichiers du répertoire courant, un par ligne. +

+ +

if

+ +
if commande ; then commandes ;
+else commandes ; fi
+ +

+Exécute l'une ou l'autre des listes de commandes, suivant que la +première commande a réussi ou non (voir ci-dessous). +

+ +

while

+ +
while commande ; do commande ; done
+ +

+Exécute les commandes de manière répétée tant que la première +commande réussit. +

+ +

case

+ +
case chaîne in
+  pattern) commande ;;
+  pattern) commande ;;
+esac
+ +

+Exécute la première commande telle que la chaîne est de la +forme pattern. Un pattern est un mot contenant +éventuellement les constructions *, ?, +[a-d], avec la même signification que pour les raccourcis dans +les noms de fichiers. Exemple : +

+ +
+case $var in
+  [0-9]*) echo 'Nombre';;
+  [a-zA-Z]*) echo 'Mot';;
+  *) echo 'Autre chose';;
+esac
+ + +

Code de retour

+ +

+On remarque que la condition des commandes if et +while est une commande. Chaque commande renvoie un code de +retour (qui est ignoré en utilisation normale). Si le code est 0, la +commande a réussi; sinon, la commande a échoué. Par exemple, le compilateur +cc renvoie un code d'erreur non nul si le fichier compilé +contient des erreurs, ou s'il n'existe pas. +

+ +

+Les commandes if et while considèrent donc le code +de retour 0 comme « vrai », et tout autre code comme « faux ». +

+ +

+Il existe une commande test, qui évalue des expressions booléennes +passées en argument, et renvoie un code de retour en fonction du résultat. +Elle est bien utile pour les scripts. Exemple : +

+ +
+if test $var = foo
+then echo 'La variable vaut foo'
+else echo 'La variable ne vaut pas foo'
+fi
+ + +

Variables

+ +

+Dans les scripts, on peut utiliser des variables définies à l'extérieur +(avec setenv ou export), mais aussi définir ses +variables locales propres au script. On donne une valeur à une variable avec +une commande de la forme +nom-de-variable=valeur. Les variables sont +utilisées pour stocker des informations. +

+ +

+On a aussi des variables spéciales, initialisées automatiquement au +début du script: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
$0Le nom de la commande (i.e. : du script)
$1, $2, etc.Le premier, deuxième, etc, argument passés au script.
$*La liste de tous les arguments passés au script.
$#Le nombre d'arguments passés au script.
$?Le code de retour de la dernière commande lancée.
$! Le numéro de process de la dernière commande lancée en tâche de fond.
$$Le numéro de process du shell lui-même.
+ + +
+Basé sur un polycopié de Roberto Di Cosmo, Xavier Leroy et Damien Doligez. +Ajustements : Nicolas George. +Dernière modification le 2002-11-16. +
+ + +