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 @@ + + + +
++Le machin qui interprète 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
+
+
+
+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. +
+ ++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. +
+ +
+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). +
+ +
+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+ +
+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
...).
+
+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
.
+
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 : +
+ +mv *.c *.bak
, car le shell va passer à
+mv
les arguments foo.c bar.c foo.bak bar.bak
, et
+mv
ne sait pas quel fichier remplacer.
+rm * ~
, le shell remplace
+l'étoile par la liste des fichiers présents, et ils seront tous effacés. Si
+vous tapez rm *~
, seuls les fichiers dont le nom finit par un tilde
+seront effacés.
+
+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.
+
+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.
+
\
)+ 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 \\\?\\\*\\\$ +\?\*\$+ +
'
)
+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 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.
+
`
)
+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
+
+
++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. +
+ +>
+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.
+
<
+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.
+
|
+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. +
+ ++La panoplie complète des redirections est la suivante (le tableau indique les +redirections qui diffèrent selon les shells) : +
+ +>
: change la sortie standard de la commande pour la placer
+dans un fichier.
+<
: change l'entrée standard de la commande pour la prendre
+dans un fichier.
+|
: branche la sortie standard de la commande de gauche sur
+l'entrée standard de la commande de droite.
+>>
: change la sortie standard pour l'ajouter à la fin
+d'un fichier existant.
+||
: exécuter la commande suivante si la première a
+échoué.
+&&
: n'exécuter la commande suivante que si la
+première a réussi.
+Fonction | +Bourne shell | +Z-shell | +
---|---|---|
Redirige la sortie d'erreur (2) et la sortie standard (1) sur l'entrée +de la commande suivante | +2>&1 | |
+ |& |
+
Redirige la sortie d'erreur et la sortie standard dans un fichier | +2>&1 > |
+ >& |
+
Redirige la sortie d'erreur et la sortie +standard à la fin d'un fichier existant | +2>&1 >> |
+>>& |
+
+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:
+
less
,
+bien plus évolué) à la fin du pipe-line, affiche le résultat page par page,
+pour laisser le temps de le lire.
+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
.
+
+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: +
+ +DISPLAY
: L'écran sur lequel les programmes X travaillent.
+C'est souvent de la forme : machine.somehost.somewhere:0.0
+Si c'est vide, c'est qu'il n'y a pas d'affichage graphique possible.
+PRINTER
: pour les commandes d'impression. Contient le nom de
+l'imprimante sur laquelle il faut envoyer vos fichiers.
+EDITOR
: utilisée par mutt
, forum
,
+et beaucoup d'autres commandes. Contient le nom de votre éditeur de textes
+préféré.
+VISUAL
: la même chose qu'EDITOR
.
+SHELL
: contient le nom de votre shell.
+HOME
: contient le nom de votre répertoire personnel.
+USER
: contient votre nom de login.
+LOGNAME
: la même chose que USER
.
+PATH
: contient une liste de répertoires dans lesquels le
+shell va chercher les commandes.
+
+Exercice : Assurez-vous que /usr/local/games/bin
se trouve
+bien dans votre PATH.
+
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.
+
+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 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 commande ; do commande ; done+ +
+Exécute les commandes de manière répétée tant que la première +commande réussit. +
+ +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+ + +
+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+ + +
+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: +
+ +$0 |
+Le 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. | +