aboutsummaryrefslogtreecommitdiff
path: root/fichiers/04-grep-bash.tex
diff options
context:
space:
mode:
Diffstat (limited to 'fichiers/04-grep-bash.tex')
-rw-r--r--fichiers/04-grep-bash.tex1226
1 files changed, 1226 insertions, 0 deletions
diff --git a/fichiers/04-grep-bash.tex b/fichiers/04-grep-bash.tex
new file mode 100644
index 0000000..48ea55f
--- /dev/null
+++ b/fichiers/04-grep-bash.tex
@@ -0,0 +1,1226 @@
1\input{../_preamble}
2\input{../_preamble_bbl}
3\usepackage{menukeys}
4\title{grep et bash}
5\usepackage{float}
6\usepackage{dingbat}
7\usepackage[newfloat]{minted}
8\SetupFloatingEnvironment{listing}{listname=Listings}
9\setminted{
10 bgcolor=Lavender,
11 breaklines,
12 breaksymbolright=\small\carriagereturn}
13\setmintedinline{bgcolor=Lavender}
14\usepackage{capt-of}
15\usepackage{soul}
16\makeindex[name=cmds, intoc, title={Liste des commandes et
17 instructions}, options={-s \jobname.ist}]
18
19\NewDocumentCommand{\commande}{s m O{}}{
20 \IfBooleanTF{#1}{\index[cmds]{#2@\texttt{#2}|#3textbf}}
21 {\index[cmds]{#2@\texttt{#2}#3}}
22}
23
24\NewDocumentCommand{\inputfile}{O{} m m O{application/x-sh}}{
25 \marginpar{\attachandlink{scripts/#3}[#4]{Fichier
26 attaché}{\textcolor{blue}{Ouvrir le fichier}}}
27 \inputminted[#1]{#2}{scripts/#3}
28}
29
30\begin{document}
31\maketitle
32\renewcommand{\contentsname}{Sommaire}
33\tableofcontents
34
35\listoflistings
36
37\needspace{3\baselineskip}
38\listoftables
39
40\chapter{grep, les expressions régulières}
41\label{cha:grep-les-expressions}\commande{grep}
42Les expressions régulières se rapprochent des \emph{wildcards} ou
43\enquote{métacaractères} qui ont été présentés dans
44\href{./01-ligne-de-commande.pdf#lnk_wildcards}{le cours sur la ligne
45 de commande}. C'est une technique commune à pour ainsi dire tous les
46langages de programmation qui permet de construire des
47\enquote{modèles}, en anglais \emph{patterns}, susceptibles de
48capturer des chaînes de caractères.
49
50Par exemple, soit le fichier suivant:
51\begin{minted}{text}
52/usr/share/dict/cracklib-small
53\end{minted}
54Ce fichier fait partie d'un programme dont le rôle est de vérifier la
55robustesse des mots de passe. Il contient un grand nombre d'entrées, à
56raison d'un mot par ligne. Vérifions cela:
57\begin{minted}{text}
58[robert@kiddo courses]$ wc -l /usr/share/dict/cracklib-small
5954763 /usr/share/dict/cracklib-small
60\end{minted}
61L'expression régulière suivante retourne tous les mots de cinq lettres
62de ce fichier qui commencent par la lettre \verb|c| et se terminent
63par la lettre \verb|h|:
64\begin{minted}{text}
65[robert@kiddo courses]$ grep '\<c...h\>' /usr/share/dict/cracklib-small
66catch
67cinch
68clash
69cloth
70coach
71conch
72couch
73cough
74crash
75crush
76czech
77\end{minted}
78
79\begin{quoting}
80 \textsc{Rem.} \verb|grep| recherche les modèles ligne par
81 ligne et retourne donc un résultat positif dès lors qu'un modèle
82 donné a été trouvé au moins une fois dans une ligne.
83\end{quoting}
84
85\paragraph{Modèles}
86Pour construire les modèles (\emph{patterns}), on peut utiliser les
87symboles suivants\footnote{Cette liste n'est pas exhaustive.}:
88\begin{xltabular}{\linewidth}{lX}
89 \toprule
90 Symbole & Signification \\ \midrule\endhead
91 \verb|.| & tout caractère unique\\
92 \verb|?| & le caractère précédent est répété 0 ou une fois\\
93 \verb|*| & le caractère précédent est répété 0 fois ou autant
94 de fois que possible\\
95 \verb|+| & le caractère précédent est répété une fois \emph{au
96 moins}\\
97 \verb|{n}| & le caractère précédent est répété exactement \emph{n}
98 fois\\
99 \verb|{n,m}| & le caractère précédent est répété au moins \emph{n}
100 fois et au plus \emph{m} fois\\
101 \verb|[abc]| & le caractère précédent est l'un de ceux qui se
102 trouvent entre les crochets droits\\
103 \verb|[^abc]| & le caractère précédent n'est pas l'un de ceux qui se
104 trouvent entre les crochets droits\\
105 \verb|[a-z]| & le caractère précédent est compris entre \emph{a} et
106 \emph{z}, dans l'ordre de la table des
107 caractères. C'est le sens du trait d'union entre les
108 lettres \verb|a| et \verb|z|. On peut bien sûr
109 combiner des chaînes avec et sans trait d'union. Par
110 exemple, \verb|[A-Ea-e]| correspond aux cinq
111 premières lettres de l'alphabet, en majuscule et en
112 minuscule. \\
113 \verb|()| & ce qui est inclus entre les parenthèses est traité comme
114 un groupe \\
115
116 \verb+|+ & opérateur logique signifiant \emph{ou} \\
117 \verb|^| & représente le début de la ligne\\
118 \verb|$| & représente la fin de la ligne\\ \\
119 \verb|\<| et \verb|\>| & représentent respectivement un début et une
120 fin de mot\\
121 \bottomrule
122 \caption{grep \emph{patterns}}
123\end{xltabular}
124
125\paragraph{grep, egrep}
126\commande*{grep}
127\commande{egrep}[|see{\texttt{grep}}]
128À la place de \verb|grep|, on peut saisir à la ligne de commande
129\verb|egrep| ou \verb|grep -E| pour \emph{extended regular
130 expressions}. Quelle est la différence? Retenez ici simplement que
131sous \verb|grep| les metacaractères
132\begin{minted}{text}
133? + { } | ( )
134\end{minted}
135doivent être précédés de la \emph{séquence d'échappement} \verb|\|
136comme ceci:
137\begin{minted}{text}
138\? \+ \{ \} \| \( \)
139\end{minted}
140tandis que cela ne se fait pas avec \verb|egrep|.
141
142\paragraph{options}
143La commande \verb|(e)grep| peut recevoir un grand nombre
144d'options. Parmi ces options, retenons celles-ci:
145\begin{description}
146\item[-n] retourne les numéros des lignes dans lesquelles le modèle de
147 recherche a été trouvé.
148\item[-c] retourne le nombre d'occurrences trouvées.
149\item[-i] demande à \verb|grep| de ne pas faire de différence entre
150 les minuscules et les majuscules.
151\item[-H] retourne le nom du fichier dans lequel le modèle recherché
152 est trouvé.
153\item[-v] \emph{nie} le modèle recherché: \verb|grep| retournera donc
154 les lignes dans lesquelles le modèle \emph{n'a pas été trouvé}.
155\end{description}
156
157\paragraph{Exemples}
158Les exemples ci-dessous utilisent
159\href{./01-ligne-de-commande.pdf#lnk_redirection}{la technique de la
160 redirection}.
161\begin{minted}{text}
162[robert@kiddo courses]$ cat /usr/share/dict/cracklib-small | grep '\<s.*m\>' | grep 'ea'
163scream
164seagram
165sealteam
166seam
167sidearm
168steam
169stream
170sunbeam
171sunbeam's
172\end{minted}
173 \begin{quoting}
174 \textbf{Commentaire:} La ligne de commande fait ici successivement
175 les opérations suivantes:
176 \begin{enumerate}
177 \item Concaténation de toutes les lignes du fichier
178 \verb|cracklib-small|
179 \item Sélection de tous les mots qui commencent par la lettre
180 \verb|s| et se terminent par la lettre \verb|m|.
181 \item Parmi ces mots, sélection de ceux qui contiennent la chaîne
182 \verb|ea|.
183 \end{enumerate}
184 \end{quoting}
185\begin{minted}{text}
186[robert@kiddo courses]$ cat /usr/share/dict/cracklib-small | grep '\<.....\>' | grep -E 'o{2}|e{2}' | grep 't' | column -c 70
187afoot fleet needn't skeet steep taboo three tweed
188beets foote roost sleet steer taboo's three's
189boost greet roots sooth stood teems tools
190booth hoots scoot steed stool teens tooth
191boots loots sheet steel stoop teeth trees
192booty meets shoot steen sweet tepee troop
193\end{minted}
194\begin{quoting}
195 \textbf{Commentaire:} La ligne de commande fait ici successivement
196 les opérations suivantes:
197 \begin{enumerate}
198 \item Concaténation de toutes les lignes du fichier
199 \verb|cracklib-small|
200 \item Sélection des mots de cinq caractères.
201 \item Parmi ces mots, sélection de ceux qui contiennent \emph{soit}
202 la chaîne \verb|oo| \emph{soit} la chaîne \verb|ee|.
203 \item Enfin, sélection, parmi ces derniers mots, de ceux qui
204 contiennent la lettre \verb|t|
205 \item La dernière ligne, dont on n'a pas étudié la syntaxe, demande
206 l'affichage du résultat sous la forme de colonnes tabulées.
207 \end{enumerate}
208\end{quoting}
209
210\chapter{bash}
211\label{cha:bash}
212Comme on l'a vu, \verb|bash| est le \emph{shell} le plus répandu sur
213les systèmes Linux aujourd'hui. On peut écrire en \verb|bash| des
214\emph{scripts}, autrement dit de petits programmes informatiques, pour
215réaliser des suites d'opérations plus ou moins complexes.
216
217Voici un exemple très simple. Prenons les lignes suivantes:
218\begin{minted}[linenos]{text}
219mkdir sauvegarde
220cp *.tex sauvegarde
221zip -r sauvegarde.zip sauvegarde
222\end{minted}
223Ces trois lignes exécutent successivement les opérations suivantes:
224\begin{enumerate}
225\item Création d'un répertoire intitulé \verb|sauvegarde|
226\item Copie de tous les fichiers \TeX{} dans le répertoire
227 \verb|sauvegarde|
228\item Création d'une archive \verb|.zip| de tout le répertoire.
229\end{enumerate}
230
231Pour éviter de répéter ces trois lignes de commande et d'encourir le
232risque de se tromper dans la saisie, on peut décider de les écrire
233dans un fichier texte que l'on appellera par exemple \verb|backup.sh|
234de la façon suivante:
235\begin{minted}[linenos]{bash}
236#!/bin/bash
237mkdir sauvegarde
238cp *.tex sauvegarde
239zip -r sauvegarde.zip sauvegarde
240\end{minted}
241
242Il suffit alors de demander à \verb|bash| d'exécuter ce fichier pour
243que les trois opérations soient réalisées d'un coup. Comme les scripts
244écrits en \verb|bash| sont interprétés par le \textsl{shell}
245\verb|bash|, \emph{toute ligne de commande peut être exécutée dans un
246 script}. Réciproquement, \emph{tout ce qui peut entrer dans un
247 script peut aussi être exécuté à la ligne de commande}.
248
249\section{L'éditeur de texte}
250\label{sec:lediteur-de-texte}
251C'est dans un \emph{éditeur de texte} que l'on saisit tout code
252informatique. Certains éditeurs de texte sont très simples à
253utiliser. Nous allons prendre ici l'exemple de l'un des plus simples,
254\verb|nano|. Pour le lancer, il suffit de saisir à la ligne de
255commande: \mintinline{text}|nano|. Après avoir lancé \verb|nano| et
256saisi le script donné ci-dessus, voici ce que l'on obtient:
257\begin{minted}[linenos,fontsize=\footnotesize]{text}
258 GNU nano 2.8.2 Nouvel espace
259
260#!/bin/bash
261mkdir sauvegarde
262cp *.tex sauvegarde
263zip -r sauvegarde.zip sauvegarde
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281^G Aide ^O Écrire ^W Chercher ^K Couper ^J Justifier ^C Pos. cur.
282^X Quitter ^R Lire fich.^\ Remplacer ^U Coller ^T Orthograp.^_ Aller lig.
283\end{minted}
284
285Les lignes 24 et 25 correspondent au menu de \verb|nano|. On n'y
286accède pas par la souris, mais à l'aide des \emph{raccourcis clavier}
287qui sont tous préfixés par le \emph{caret} (\verb|^|) qui représente
288la touche \keys{Ctrl} du clavier. Donc pour quitter le programme, on
289appuiera sur \keys{Ctrl-X}: voici ce que montre \verb|nano| au bas du
290terminal après avoir saisi \keys{Ctrl-X}:
291\begin{minted}[linenos,fontsize=\footnotesize]{text}
292Écrire l'espace modifié ? (Répondre « Non » ABANDONNE les modifications.)
293 O Oui
294 N Non ^C Annuler
295\end{minted}
296Les opérations suivantes sont donc possibles:
297\begin{enumerate}
298\item \keys{O}: sauvegarde le fichier.
299\item \keys{N}: quitte \verb|nano| sans sauvegarder.
300\item \keys{Ctrl-C}: annule l'opération et retourne à l'éditeur de texte.
301\end{enumerate}
302Appuyons sur la touche \keys{O}. \verb|nano| nous invite alors à
303entrer le nom du script:
304\begin{minted}[linenos,fontsize=\footnotesize]{text}
305Nom du fichier à écrire: backup.sh
306^G Aide M-D Format DOS M-A Ajout (à la fin)M-B Copie de sécu.
307^C Annuler M-M Format Mac M-P Ajout (au début)^T Parcourir
308\end{minted}
309Après avoir entré le nom du fichier et appuyé sur la touche
310\keys{Enter} pour confirmer le choix, on retourne au terminal et à la
311ligne de commande.
312
313\section{Le \emph{shebang}}
314\label{sec:le-shebang}
315La première ligne du script \verb|backup.sh| donné en exemple
316ci-dessus appelle un commentaire particulier:
317\begin{minted}[linenos]{bash}
318#!/bin/bash
319\end{minted}
320
321Dans cette ligne, la séquence \mintinline{bash}|#!| s'appelle le
322\emph{shebang}. Par convention, le \emph{shebang} est un préfixe que
323l'on fait suivre du nom du programme qui doit interpréter le script,
324précédé de son chemin d'accès absolu.
325
326Le \emph{shebang} est important car il permet d'accéder aux
327interpréteurs auxquels on souhaite accéder depuis la ligne de
328commande. Par exemple, pour un script écrit en Python2, la première
329ligne sera:
330\begin{minted}[linenos]{python}
331#!/usr/bin/env python2
332"""
333 Mon premier script en Python
334"""
335print("bonjour le monde!")
336\end{minted}
337
338\section{Les commentaires}
339\label{sec:les-commentaires}
340En \verb|bash|, tout ce qui, sur une même ligne, suit le signe
341\mintinline{bash}|#| \emph{n'est ni interprété, ni exécuté}. On
342utilise donc ce signe pour introduire des \emph{commentaires} dans le
343code informatique.
344
345Les commentaires ne servent pas uniquement à introduire des remarques
346pour son usage personnel. Ils servent aussi, et surtout, à donner des
347indications sur le code lui-même pour permettre aux autres de mieux
348comprendre la programmation. Si le code est bien commenté, alors on
349doit pouvoir le lire comme on lit un livre. Cela est très important
350car les programmes longs et complexes dépassent souvent et parfois
351même survivent à leur auteur.
352
353Si le code est bien compris, il sera facilement mis à jour, corrigé et
354augmenté par d'autres programmeurs.
355
356L'art de commenter le code informatique tout en l'écrivant porte le
357nom de \emph{literate programming}. Il a été inventé par Donald
358E.~Knuth, le créateur de \TeX, qui a posé tous les principes de cet
359art dans le cadre de la programmation en
360\textsf{WEB}\footnote{\cite{Knuth1983}. Voir également en ligne
361 \url{http://www.literateprogramming.com/}}.
362
363Pour un exemple de code commenté, voir le \vref{lst:copyten}.
364
365\section{Exécution}
366\label{sec:execution}
367Il faut ici approfondir la notion de \emph{permissions} sur les
368fichiers qui a été présentée dans le cours sur la
369\href{./01-ligne-de-commande.pdf#lnk_permissions}{ligne
370 de commande}. Nous avons en effet étudié trois types de permissions
371sur les fichiers: en lecture, en écriture et en exécution. Revenons
372sur les permissions données par défaut au script \verb|backup.sh|:
373\begin{minted}{text}
374[robert@kiddo courses]$ ls -l backup.sh
375-rw-r--r-- 1 robert robert 82 17 sept. 22:06 backup.sh
376\end{minted}
377Soit:
378\begin{itemize}
379\item lecture et écriture pour l'utilisateur \verb|robert| (\verb|rw|);
380\item lecture seule pour le groupe \verb|robert| (\verb|r|);
381\item lecture seule pour le reste du monde (\verb|r|)
382\end{itemize}
383
384\paragraph{chmod}
385\commande*{chmod}
386La commande qui permet de changer les droits s'appelle
387\verb|chmod|. Pour comprendre comment l'utiliser, il faut savoir que
388les permissions sont traduites par des valeurs numériques, à savoir:
389\begin{itemize}
390\item 4 pour le droit \emph{lecture};
391\item 2 pour le droit \emph{écriture};
392\item 1 pour le droit \emph{exécution}.
393\end{itemize}
394Ces valeurs peuvent être additionnées. On analyse donc ainsi les
395permissions sur le fichier \verb|backup.sh|:
396\begin{itemize}
397\item utilisateur \verb|robert|, lecture + écriture: $4+2=6$;
398\item groupe \verb|robert|, lecture: $4$;
399\item reste du monde, lecture: $4$.
400\end{itemize}
401Soit $644$. Pour ajouter à l'utilisateur \verb|robert| seulement la
402permission en exécution, il faudrait donc porter cette valeur à
403$744$. Nous allons ici donner ce droit à la fois à \verb|robert|, au
404groupe \verb|robert| et au reste du monde, soit une valeur de
405$755$. La syntaxe est la suivante:
406\begin{minted}{text}
407chmod xyz <fichier>
408\end{minted}
409où \verb|xyz| sont les trois chiffres qui représentent les permissions.
410\begin{minted}[escapeinside=||, linenos]{text}
411[robert@kiddo courses]$ chmod 755 backup.sh
412[robert@kiddo courses]$ ls -l --color backup.sh
413-rwxr-xr-x 1 robert robert 82 17 sept. 22:06 |\textcolor{green}{backup.sh}|
414\end{minted}
415\begin{quoting}
416 \textbf{Commentaire}:
417 \begin{enumerate}
418 \item La commande \verb|chmod| a été entrée à la ligne 1.
419 \item À la ligne 2, nous avons lancé la commande
420 \commande{ls}\mintinline{text}|ls -l --color| sur le fichier
421 \verb|backup.sh|: les droits listés à la ligne 3 montrent bien que
422 la valeur \verb|x| a été ajoutée aux trois endroits possibles. On
423 voit enfin que l'option \verb|--color| affiche en vert les
424 fichiers qui sont exécutables.
425 \end{enumerate}
426\end{quoting}
427
428Nous pouvons désormais exécuter notre script:
429\begin{minted}[linenos,escapeinside=||]{text}
430[robert@kiddo courses]$ ls -l --color
431total 36
432-rwxr-xr-x 1 robert robert 82 17 sept. 22:06 |\textcolor{green}{backup.sh}|
433-rw-r--r-- 1 robert robert 165 16 sept. 19:40 bibliography.bib
434drwxr-xr-x 5 robert robert 4096 17 sept. 22:30 |\textcolor{blue}{fichiers}|
435-rw-r--r-- 1 robert robert 680 16 sept. 18:34 makefile
436-rw-r--r-- 1 robert robert 898 16 sept. 19:39 _preamble_bbl.tex
437-rw-r--r-- 1 robert robert 699 14 sept. 15:02 _preamble-ed.tex
438-rw-r--r-- 1 robert robert 719 16 sept. 19:39 _preamble.tex
439-rw-r--r-- 1 robert robert 1407 17 sept. 00:15 README.md
440-rw-r--r-- 1 robert robert 1804 17 sept. 00:15 README.tex
441[robert@kiddo courses]$ ./backup.sh
442 adding: sauvegarde/ (stored 0%)
443 adding: sauvegarde/README.tex (deflated 57%)
444 adding: sauvegarde/_preamble.tex (deflated 45%)
445 adding: sauvegarde/_preamble_bbl.tex (deflated 57%)
446 adding: sauvegarde/_preamble-ed.tex (deflated 44%)
447[robert@kiddo courses]$ ls -l --color
448total 44
449-rwxr-xr-x 1 robert robert 82 17 sept. 22:06 |\textcolor{green}{backup.sh}|
450-rw-r--r-- 1 robert robert 165 16 sept. 19:40 bibliography.bib
451drwxr-xr-x 5 robert robert 4096 17 sept. 22:31 |\textcolor{blue}{fichiers}|
452-rw-r--r-- 1 robert robert 680 16 sept. 18:34 makefile
453-rw-r--r-- 1 robert robert 898 16 sept. 19:39 _preamble_bbl.tex
454-rw-r--r-- 1 robert robert 699 14 sept. 15:02 _preamble-ed.tex
455-rw-r--r-- 1 robert robert 719 16 sept. 19:39 _preamble.tex
456-rw-r--r-- 1 robert robert 1407 17 sept. 00:15 README.md
457-rw-r--r-- 1 robert robert 1804 17 sept. 00:15 README.tex
458drwxr-xr-x 2 robert robert 4096 17 sept. 22:31 |\textcolor{blue}{sauvegarde}|
459-rw-r--r-- 1 robert robert 2828 17 sept. 22:31 sauvegarde.zip
460[robert@kiddo courses]$ ls sauvegarde
461_preamble_bbl.tex _preamble-ed.tex _preamble.tex README.tex
462\end{minted}
463\begin{quoting}
464 \textbf{Commentaire:}
465 \begin{itemize}
466 \item lignes 1--11: la commande \commande{ls}\verb|ls -l --color|
467 donne l'état du dossier \emph{avant} l'exécution du script
468 \verb|backup.sh|;
469 \item lignes 12--17: exécution du script et messages du terminal;
470 \item lignes 18--30: la commande \verb|ls -l --color| donne l'état du
471 dossier \emph{après} l'exécution du script \verb|backup.sh|. On
472 voit qu'un nouveau répertoire \verb|sauvegarde| a été créé, de
473 même qu'un fichier archive \verb|sauvegarde.zip|;
474 \item lignes 31--32: la commande \verb|ls sauvegarde| liste le
475 contenu de ce répertoire. On y trouve tous les fichiers
476 \verb|.tex| qui y ont été copiés par le script.
477 \end{itemize}
478\end{quoting}
479
480\paragraph{PATH} Un dernier point reste à éclaircir: à la ligne 12,
481pourquoi a-t-on écrit \mintinline{bash}|./backup.sh| et non pas
482simplement \mintinline{bash}|backup.sh|? Tout simplement pour des
483raisons de sécurité. En effet, le principe est que les fichiers
484exécutables se trouvent dans certains répertoires-système spécialement
485conçus pour les accueillir. C'est pour cette raison que l'on peut
486lancer les commandes \verb|bash| sans avoir à les préfixer. Or notre
487répertoire de travail ne fait partie de ces répertoires spéciaux. Il
488faut donc préfixer tout script exécutable qui s'y trouve par son
489\emph{chemin d'accès}, soit relatif, soit absolu. On a choisi ici la
490première méthode: dans la séquence \mintinline{text}|./|, le point
491représente le répertoire courant tandis que le \emph{slash} précise
492qu'il s'agit d'un chemin d'accès. Sans le \emph{slash}, le
493\emph{shell} aurait compris le point comme un préfixe de fichier
494caché.
495
496\section{Les variables}
497\label{sec:les-variables}
498Les variables sont des informations temporaires qui peuvent être
499stockées et rappelées à tout moment. L'exemple qui suit va donner
500l'occasion d'étudier une nouvelle commande,
501\commande{echo}\verb|echo|, dont le rôle est justement de retourner
502au terminal la chaîne de caractères qu'on lui passe en argument:
503\begin{minted}[linenos]{text}
504[robert@kiddo courses]$ mysystem="Linux"
505[robert@kiddo courses]$ echo 'Mon système est $mysystem.'
506Mon système est $mysystem.
507[robert@kiddo courses]$ echo "Mon système est $mysystem."
508Mon système est Linux.
509\end{minted}
510
511\begin{quoting}
512 \textbf{Commentaire:}
513 \begin{description}
514 \item[Définition] Pour \emph{définir une variable}, on lui donne un
515 nom, suivi du signe $=$, suivi de sa définition entre guillemets
516 droits.
517 \item[Rappel] Pour \emph{rappeler} la valeur d'une variable, on
518 saisit le nom qu'on lui a donné, préfixé par le signe \verb|$|.
519 \end{description}
520 \begin{mdframed}[backgroundcolor=Cyan]
521 Observez la différence dans l'usage des guillemets!
522 \href{./01-ligne-de-commande.pdf#lnk_guillemets}{Comme
523 on le sait}, les guillemets servent à indiquer au \emph{shell}
524 que les espaces ne sont pas des caractères actifs. Il y a
525 cependant une grande différence entre les guillemets simples et
526 les guillemets doubles: les premiers renvoient une expression
527 littérale dans laquelle rien n'est interprété (lignes~2--3) tandis
528 que les seconds permettent l'interprétation des variables
529 (lignes~4--5).
530 \end{mdframed}
531\end{quoting}
532
533Certaines variables sont automatiquement définies par \emph{bash}. Par
534exemple:
535\begin{enumerate}
536\item \verb|$0|: renvoie le nom du script en cours d'exécution.
537\item \verb|$1 - $9|: désignent les arguments successifs passés au
538 script.
539\end{enumerate}
540
541Nous pouvons désormais perfectionner le script \verb|backup.sh|:
542\begin{minted}[linenos]{bash}
543#!/bin/bash
544echo "Veuillez choisir l'extension des fichiers à sauvegarder"
545echo "(sans le point):"
546read -p 'extension: ' ext
547echo "Veuillez choisir le nom du dossier de sauvegarde:"
548read -p 'dossier: ' backupdir
549mkdir "$backupdir"
550cp *.$ext "$backupdir"
551zip -r "$backupdir".zip "$backupdir"
552echo "Terminé. $0 a copié vos fichiers .$ext dans $backupdir"
553echo "et l'archive $backupdir.zip a été créée."
554\end{minted}
555
556\paragraph{read}
557\label{ref:read}
558\commande*{read} Cette nouvelle commande est expliquée dans
559le commentaire qui suit\footnote{Voir aussi plus loin
560 \vpageref{ref:read-plus}.}:
561\begin{quoting}
562 \textbf{Commentaire:}
563 \begin{enumerate}
564 \item Une nouvelle commande a été introduite ici: aux lignes~4 et 6,
565 \verb|read| attend que l'utilisateur saisisse quelque chose au
566 clavier. Ensuite, la valeur saisie au clavier est associée à une
567 variable qui peut être reprise dans le script. On a également
568 passé à \verb|read| l'option \verb|-p| qui permet d'ajouter un
569 \emph{prompt} spécifique.
570 \item Aux lignes 7, 8 et 9, on a pris la précaution de mettre entre
571 guillemets doubles le rappel de la variable:
572 \verb|"$backupdir"|. On sait en effet que beaucoup d'utilisateurs
573 utilisent les espaces dans les noms des fichiers. Ici, la variable
574 étant rappelée à l'intérieur de guillemets doubles, elle sera
575 toujours interprétée correctement par \emph{bash}.
576 \end{enumerate}
577\end{quoting}
578
579Exécution du script \verb|./backup.sh tex|:
580\begin{minted}[linenos]{text}
581[robert@kiddo courses]$ ./backup.sh
582Veuillez choisir l'extension des fichiers à sauvegarder
583(sans le point):
584extension: tex
585Veuillez choisir le nom du dossier de sauvegarde:
586dossier: Houba
587 adding: Houba/ (stored 0%)
588 adding: Houba/README.tex (deflated 57%)
589 adding: Houba/_preamble.tex (deflated 45%)
590 adding: Houba/_preamble_bbl.tex (deflated 57%)
591 adding: Houba/_preamble-ed.tex (deflated 44%)
592Terminé. ./backup.sh a copié vos fichiers .tex dans Houba
593et l'archive Houba.zip a été créée.
594\end{minted}
595\begin{quoting}
596 \textbf{Commentaire:}
597 \begin{enumerate}
598 \item Les valeurs attendues ont été saisies par l'utilisateur aux
599 lignes~4 et 6.
600 \item À la ligne~12, le script a utilisé la variable \verb|$0| pour
601 retourner son propre nom.
602 \end{enumerate}
603\end{quoting}
604
605\section{Captures et substitutions}
606\label{sec:capt-et-subst}
607Cette technique simple permet de transformer en variable le résultat
608d'une commande. Il suffit de placer la commande entre parenthèses
609précédées du signe $=$. Exemple:
610\begin{minted}[linenos]{text}
611[robert@kiddo courses]$ nbre=$(ls | wc -l)
612[robert@kiddo courses]$ echo $nbre
6138
614\end{minted}
615
616\needspace{2\baselineskip}
617\begin{quoting}
618 \textbf{Commentaire:}
619 \begin{enumerate}
620 \item À la ligne~1, la commande \commande{ls}\verb|ls| liste les
621 fichiers, puis son résultat est interprété par
622 \commande{wc}\verb|wc -l| qui compte le nombre de fichiers
623 retournés.
624 \item La variable \verb|$nbre| contient donc ce dernier résultat.
625 \end{enumerate}
626\end{quoting}
627
628Utilisons cette technique pour perfectionner notre script
629\verb|backup.sh| (voir ci-dessous la ligne~9):
630\begin{minted}[linenos]{bash}
631#!/bin/bash
632echo "Veuillez choisir l'extension des fichiers à sauvegarder"
633echo "(sans le point):"
634read -p 'extension: ' ext
635echo "Veuillez choisir le nom du dossier de sauvegarde:"
636read -p 'dossier: ' backupdir
637mkdir "$backupdir"
638cp *.$ext "$backupdir"
639nbre=$(ls $backupdir/*.$ext | wc -l)
640zip -r "$backupdir".zip "$backupdir"
641echo "Terminé. $0 a copié $nbre fichiers .$ext dans $backupdir"
642echo "et l'archive $backupdir.zip a été créée."
643\end{minted}
644
645Exécution:
646\begin{minted}[linenos]{text}
647[robert@kiddo courses]$ ./backup.sh
648Veuillez choisir l'extension des fichiers à sauvegarder
649(sans le point):
650extension: tex
651Veuillez choisir le nom du dossier de sauvegarde:
652dossier: Houba
653 adding: Houba/ (stored 0%)
654 adding: Houba/README.tex (deflated 57%)
655 adding: Houba/_preamble.tex (deflated 45%)
656 adding: Houba/_preamble_bbl.tex (deflated 57%)
657 adding: Houba/_preamble-ed.tex (deflated 44%)
658Terminé. ./backup.sh a copié 4 fichiers .tex dans Houba
659et l'archive Houba.zip a été créée.
660\end{minted}
661
662\section{Conditions}
663\label{sec:conditions}
664Il est souvent très utile de n'exécuter des lignes de code que si
665certaines conditions sont remplies. Reprenons ici l'exemple du script
666\verb|backup.sh|: au moment où le script crée le répertoire de
667sauvegarde, il ne contrôle pas si ce répertoire existe déjà, ce qui est
668un défaut. Voici comment on peut créer une condition adaptée à cette
669situation:
670\begin{minted}[linenos]{bash}
671if [ -d "$backupdir" ]
672then
673 echo "Le dossier $backupdir existe déjà. Veuillez relancer le"
674 echo "programme et saisir un autre nom."
675 exit 1
676else
677 mkdir "$backupdir"
678fi
679\end{minted}
680\begin{quoting}
681 \textbf{Commentaire:}
682 \begin{enumerate}
683 \item Observez la structure de la condition:
684 \begin{itemize}
685 \item ligne 1: \commande*{if}\mintinline{bash}|if|
686 $\rightarrow$ \emph{si <condition>} où la condition est posée
687 entre crochets. Dans l'expression entre les crochets, %
688 \verb|-d "$backupdir"|, \verb|-d| signifie: \verb|$backupdir|
689 existe \emph{et} est un répertoire;
690 \item ligne 2: \commande*{then}\mintinline{bash}|then|
691 $\rightarrow$ \emph{alors, passez à la ligne suivante};
692 \item ligne 6: \commande*{else}\mintinline{bash}|else|
693 $\rightarrow$ \emph{autrement, passez à la ligne suivante};
694 \item ligne 8: \commande*{fi}\mintinline{bash}|fi|
695 $\rightarrow$ \emph{fin de la condition} (en fait les deux
696 lettres de la conjonction \verb|if| écrite à l'envers).
697 \end{itemize}
698 \item À la ligne 5, la commande \commande{exit}\verb|exit 1|
699 ordonne au programme de se terminer immédiatement et de renvoyer
700 la valeur \verb|1| comme numéro d'état (\emph{status number}). Par
701 convention, \verb|0| est pour \emph{true} (le programme a bien été
702 exécuté) et \verb|1| est pour \emph{false} (il y a eu une erreur).
703 \end{enumerate}
704\end{quoting}
705
706Voici donc comment se présente le script \verb|backup.sh| une fois la
707condition insérée:
708\captionof{listing}{bash: exemple de condition \texttt{if-then-else}%
709\label{lst:if-then-else}}
710\inputfile[linenos,highlightlines={7-14}]{bash}{backup.sh}
711
712\subsection{Conditions en série}
713\label{sec:conditions-en-serie}
714Supposons que le répertoire de sauvegarde ait été supprimé mais que
715l'archive \verb|.zip| correspondante ne l'ait pas été. Dans ce cas, le
716script \verb|backup.sh| l'écraserait. Pour éviter cela, nous pouvons
717utiliser dans le script l'instruction
718\commande*{elif}\verb|elif| qui permet de construire des
719conditions en série. Littéralement, \verb|elif| est pour \emph{else
720 if}, \enquote{ou autrement, si\ldots}. Voici donc ce qu'il faut
721ajouter:
722\begin{minted}{bash}
723elif [ -e "$backupdir".zip ]
724 then
725 echo "L'archive $backupdir.zip existe déjà. Veuillez la supprimer"
726 echo "ou la déplacer en dehors de ce dossier, puis relancez le"
727 echo "programme."
728 exit 1
729\end{minted}
730
731Ce qui donne le script \verb|backup-mk2.sh| suivant:
732\captionof{listing}{bash: instruction \texttt{elif}\label{lst:elif}}
733\inputfile[linenos,highlightlines={12-17}]{bash}{backup-mk2.sh}
734
735\subsection{Tests}
736\label{sec:tests}
737Le tableau suivant donne la liste des tests les plus répandus que l'on
738peut associer aux conditions. Les tests sont placés entre crochets
739droits comme le montre la ligne~7 du \vref{lst:if-then-else}.
740\begin{xltabular}{.77\linewidth}{lX}
741 \toprule
742 Opérateur & Description \\
743 \midrule\endhead
744 \verb|! expr| & \verb|expr| est faux \\
745 \verb|-n str| & la longueur de \verb|str| $>0$\footnote{Comprendre:
746 \texttt{str} existe.} \\
747 \verb|-z str| & la longueur de \verb|str| $=0$\footnote{Comprendre:
748 \texttt{str} n'existe pas.}\\
749 \verb|str1 = str2| & \verb|str1| est égal à \verb|str2|\\
750 \verb|str1 != str2| & \verb|str1| n'est pas égal à \verb|str2|\\
751 \verb|int1 -eq int2| & les nombres \verb|int1| et \verb|int2| sont
752 égaux\\
753 \verb|int1 -gt int2| & \verb|int1| $>$ \verb|int2|\\
754 \verb|int1 -lt int2| & \verb|int1| $<$ \verb|int2|\\
755 \verb|-d fichier| & \verb|fichier| existe et est un répertoire\\
756 \verb|-e fichier| & \verb|fichier| existe \\
757 \verb|-s fichier| & \verb|fichier| existe et n'est pas vide (taille
758 $>0$) \\
759 \verb|-r fichier| & \verb|fichier| existe et est accessible en
760 lecture\\
761 \verb|-w fichier| & \verb|fichier| existe et est accessible en
762 écriture \\
763 \verb|-x fichier| & \verb|fichier| existe et est accessible en
764 exécution \\
765 \bottomrule
766 \caption{tests\label{tab:tests}}
767 \label{tab:tests}
768\end{xltabular}
769
770\paragraph{test}
771\commande*{test} À l'intérieur du script \verb|bash|, les
772crochets renvoient en fait à une commande par ailleurs disponible:
773\verb|test|. La commande \verb|test| renvoie en fait la sortie
774\verb|0| si le résultat est \emph{vrai} et \verb|1| si le résultat est
775\emph{faux}. Le terminal ne retourne pas le resultat, mais celui-ci
776est associé à une variable
777\verb|$?| que l'on peut afficher par la commande:
778\commande{echo}\mintinline{bash}|echo $?|. En voici quelques
779exemples:
780\begin{minted}[linenos]{text}
781[robert@kiddo courses]$ ls
782bibliography.bib makefile _preamble-ed.tex _preamble.tex README.tex
783fichiers _preamble_bbl.tex _preamble.log README.md
784[robert@kiddo courses]$ test -d fichiers
785[robert@kiddo courses]$ echo $?
7860
787[robert@kiddo courses]$ test -x makefile
788[robert@kiddo courses]$ echo $?
7891
790[robert@kiddo courses]$ test -e makefile
791[robert@kiddo courses]$ echo $?
7920
793[robert@kiddo courses]$ test 0123 = 123
794[robert@kiddo courses]$ echo $?
7951
796[robert@kiddo courses]$ test 0123 -eq 123
797[robert@kiddo courses]$ echo $?
7980
799\end{minted}
800\begin{quoting}
801 \textbf{Commentaire ---} Cet exemple montre quelle est la différence
802 entre le test $=$ pour qui il faut une stricte équivalence dans les
803 chaînes de caractères, et le test \verb|-eq| qui est un test
804 arithmétique.
805\end{quoting}
806
807\subsection{Indentation}
808\label{sec:indentation}
809L'indentation est une technique qui consiste à donner aux lignes de
810code différentes profondeurs de marge à gauche de façon à distinguer
811clairement des blocs logiques. Les lignes 7--20 du
812\vref{lst:if-then-else} en donnent un exemple. L'indentation permet de
813faire apparaître clairement ce qui dépend de l'instruction
814\commande{if}\verb|if| (l.~7), puis \commande{elif}\verb|elif|
815(l.~12) et enfin \commande{else}\verb|else| (l.~18).
816
817Cette technique est commune à tous les langages informatiques et tous
818les programmeurs l'utilisent. En Python, que nous étudierons plus
819tard, l'indendation fait même partie du langage lui-même, puisque
820Python a recours à l'indentation pour structurer les blocs logiques, à
821la différence d'autres langages qui ont recours soit à des
822déclarations explicites telles que \verb|begin ... end| ou encore à
823des opérateurs tels que \verb|{ ... }|
824
825Voici un exemple d'indentation d'une structure dans laquelle on a
826placé une condition à l'intérieur d'une autre condition:
827\captionof{listing}{bash: exemple d'indentation}
828\inputfile[linenos]{bash}{greaterthan.sh}
829
830\subsection{Opérateurs booléens}
831\label{sec:operateurs-booleens}
832Les opérateurs booléens, dont le nom est tiré du mathématicien anglais
833George Boole, sont des opérateurs logiques que l'on peut associer à
834des conditions. En voici deux ici:
835\begin{enumerate}
836\item \verb+||+ pour \emph{ou};
837\item \verb|&&| pour \emph{et}.
838\end{enumerate}
839
840Par exemple, dans le \vref{lst:elif} ci-dessus, on pourrait remplacer
841les lignes 7--20 par le code ci-dessous:
842\begin{minted}[linenos]{bash}
843if [ -d "$backupdir" ] || [ -e "$backupdir".zip ]
844 then
845 echo "Le dossier $backupdir et/ou l'archive $backupdir.zip"
846 echo "existent déjà. Veuillez relancer le programme et saisir"
847 echo "un autre nom."
848 exit 1
849else
850 mkdir "$backupdir"
851fi
852\end{minted}
853
854\subsection{case}
855\label{sec:case}\commande*{case}
856\verb|case| est une instruction qui permet d'exécuter différentes
857actions en fonction de la valeur d'une variable. Elle est intéressante
858à étudier ici car elle fait appel à la fois à la notion de variable et
859aux expressions régulières. La syntaxe est la suivante:
860\begin{minted}[linenos]{bash}
861case $var in
862 expr1)
863 <commandes ...>
864 ;;
865 expr2)
866 <commandes ...>
867 ;;
868esac
869\end{minted}
870\begin{quoting}
871 \textbf{Commentaire:}
872 \begin{enumerate}
873 \item L'instruction \verb|case| se termine par \verb|esac|, de même,
874 comme on l'a vu, que l'expression \verb|if| se termine par
875 \verb|fi|.
876 \item Les expressions régulières sont suivies d'une parenthèse
877 fermante.
878 \item Pour passer d'un jeu de commande au jeu suivant, on écrit, sur
879 une ligne \verb|;;|
880 \end{enumerate}
881\end{quoting}
882
883Le \vref{lst:case} montre un exemple facile à comprendre de cette
884technique.
885\captionof{listing}{bash: instruction \texttt{case}\label{lst:case}}
886\inputfile[linenos]{bash}{animal.sh}
887\begin{quoting}
888 \textbf{Commentaire:}
889 \begin{enumerate}
890 \item À la ligne 4, l'utilisateur est invité à entrer une
891 réponse. L'instruction \commande{read}\verb|read| associe la
892 réponse à la variable \verb|animal|.
893 \item À la ligne 6, l'instruction \verb|case| reprend la variable
894 qui est donc préfixée par le signe \verb|$|.
895 \item Aux lignes 7 et 10, on permet à l'utilisateur d'entrer soit le
896 mot \emph{chien}, soit le mot \emph{chat}. L'initiale peut être
897 une minuscule ou une majuscule.
898 \item À la ligne 13, comme le signe \verb|*| est en \emph{bash} le
899 \emph{wildcard} qui représente toute séquence de caractères, la
900 commande de la ligne~14 sera forcément exécutée si les tests des
901 lignes~7 et~10 ont échoué.
902 \end{enumerate}
903\end{quoting}
904
905\begin{quoting}
906 \textbf{Remarque:} à la place de l'instruction \verb|case|, on
907 recommande aujourd'hui d'utiliser une nouvelle instruction,
908 \commande{switch}\verb|switch|, dont la syntaxe, plus complexe,
909 n'est pas étudiée ici.
910\end{quoting}
911
912\section{Boucles}
913\label{sec:boucles}
914Les boucles (en anglais: \emph{loops}) servent à indiquer qu'une série
915d'instructions doit reprendre et continuer à s'exécuter aussi
916longtemps qu'une condition donnée est remplie ou n'est pas remplie.
917
918Prenons un exemple simple. Nous avons dans un répertoire plusieurs
919centaines d'images de différents formats et nous souhaitons convertir
920au format \verb|.png| toutes les images qui sont enregistrées au
921format \verb|.tiff|.
922
923Pour convertir une seule image, nous pouvons utiliser l'outil en ligne
924de commande \verb|convert| fourni par le programme
925\emph{imagemagick}\footnote{\url{http://www.imagemagick.org}}. Pour
926convertir une seule image, la syntaxe est la suivante:
927\begin{minted}{bash}
928convert image.tiff image.png
929\end{minted}
930
931Mais comment faire pour en convertir un grand nombre pris dans un
932répertoire qui en compte des centaines enregistrées dans des formats
933différents?
934
935\paragraph{basename}
936\commande*{basename}Avant de continuer, il faut dire un mot de la
937commande \verb|basename| que nous allons utiliser ici. Cette commande
938permet de dépouiller un nom de fichier de son chemin d'accès et de son
939extension. La syntaxe est la suivante:
940\begin{minted}[escapeinside=||]{text}
941basename -s .|\emph{ext}| |\emph{file}|
942\end{minted}
943où \emph{ext} est le nom de l'extension et \emph{file} le nom du
944fichier. Voici un exemple:
945\begin{minted}{text}
946[robert@kiddo fichiers]$ ls -l images/
947total 252
948-rw-r--r-- 1 robert robert 96404 2 juil. 18:32 02-ascii.png
949-rw-r--r-- 1 robert robert 33951 20 juin 19:59 02-donnees1.png
950-rw-r--r-- 1 robert robert 11503 20 juin 20:01 02-donnees2.png
951-rw-r--r-- 1 robert robert 17450 3 juil. 17:43 02-exercice_formats.png
952-rw-r--r-- 1 robert robert 87510 14 sept. 15:06 02-unicode.png
953[robert@kiddo fichiers]$ basename -s .png images/*
95402-ascii
95502-donnees1
95602-donnees2
95702-exercice_formats
95802-unicode
959\end{minted}
960
961\paragraph{for-do-done}
962\commande*{for}\commande*{do}\commande*{done}
963La boucle que nous allons utiliser fait appel à trois instructions:
964\begin{enumerate}
965\item \verb|for| prend comme argument un nom qui sera ensuite traité
966 comme une variable. Le nom de la variable est suivi de l'instruction
967 \commande{in}\verb|in| et d'un nom de fichier qui contient des
968 données séparées par des espaces ou bien d'une expression dans
969 laquelle on a placé des
970 \href{./01-ligne-de-commande.pdf#lnk_wildcards}{\emph{wildcards}}.
971\item \verb|do| est suivi des commandes qu'il faut exécuter sur chaque
972 élément retourné par l'instruction \verb|for| \ldots{} \verb|in|.
973\item \verb|done| marque la fin de la boucle.
974\end{enumerate}
975
976Voici maintenant le script qui assure la conversion du format
977\verb|.tiff| vers le format \verb|.png|:
978\captionof{listing}{bash: \texttt{for ... do ... done}}
979\inputfile[linenos]{bash}{tiff2png.sh}
980
981\begin{quoting}
982 \textbf{Commentaire:}
983 \begin{enumerate}
984 \item Comprendre ainsi le début de la ligne~2 %
985 (\mintinline{bash}|for file in|): \enquote{pour tout \texttt{file}
986 dans\ldots} où \verb|file| définit une variable dont la valeur
987 pourra ensuite être rappelée par \verb|$file|. Le reste de la
988 ligne est une \emph{double capture} (v.~\emph{supra},
989 \vref{sec:capt-et-subst}):
990 \begin{enumerate}
991 \item \verb|$(ls *.tiff)| liste et retourne tous les fichiers dont
992 l'extension est \verb|.tiff|
993 \item \verb|$(basename -s .tiff $(ls *.tiff))| demande à
994 \verb|basename| de dépouiller tous ces fichiers de leur
995 extension \verb|.tiff| et retourne la liste des noms seuls.
996 \end{enumerate}
997 \item La variable \verb|file| est donc une liste constituée des noms
998 de tous les fichiers \verb|.tiff| sans leur extension. La ligne~3
999 les convertit tous les uns après les autres vers le format
1000 \verb|.png|.
1001 \end{enumerate}
1002\end{quoting}
1003
1004\paragraph{while}
1005\commande*{while} L'expression \verb|while| exécute les
1006lignes de code qui suivent aussi longtemps qu'un test donné retourne
1007un résultat positif (\enquote{vrai}, en anglais \emph{true}). Le
1008script \verb|countlines.sh|, donné dans le \cref{lst:countlines}
1009ci-dessous, utilise cette expression pour compter les lignes des
1010fichiers\footnote{Pour une autre façon plus simple d'écrire le même
1011 programme, voir le \vref{lst:countlines-mk2}.}.
1012\captionof{listing}{bash: \texttt{while}\label{lst:countlines}}
1013\inputfile[linenos,highlightlines={13,16-17}]{bash}{countlines.sh}
1014
1015\paragraph{read}
1016\label{ref:read-plus}\commande*{read}
1017On a déjà présenté plus haut \vpageref{ref:read} cette
1018instruction. Pour bien comprendre le \cref{lst:countlines}, il faut
1019savoir que la fonction de \verb|read| est de \emph{lire une ligne},
1020quelle que soit cette ligne, depuis l'entrée standard du \emph{shell}
1021(en anglais: \emph{standard input}). Dans le \vref{lst:case}, la ligne
1022en question était donc constituée d'une saisie au clavier dans
1023laquelle la ligne était formée par une chaîne de caractères terminée
1024par un \emph{retour charriot} (\emph{carriage return}).
1025
1026\begin{mdframed}[backgroundcolor=Cyan]
1027 \verb|read| admet aussi une option \verb|-r| qui est importante:
1028 quand cette option est activée, le caractère \verb|\|
1029 (\emph{backslash}) est traité comme un caractère ordinaire, et non
1030 comme un caractère actif. Pour simplement compter les lignes d'un
1031 fichier, il faut ajouter cette option car en \emph{bash} le
1032 \emph{backslash} est précisément un caractère actif: à la fin d'une
1033 ligne, il sert à joindre cette ligne à la ligne suivante.
1034\end{mdframed}
1035
1036Étudions de près les lignes~13--17 du \cref{lst:countlines}:
1037l'instruction \verb|while| (l.~13) se termine à la ligne~17 par
1038\verb|done|. Et aussitôt à ce moment,
1039\href{./01-ligne-de-commande.pdf#lnk_redirection}{l'opérateur
1040 de redirection} \mintinline{bash}|<| lit le contenu du fichier dont
1041le nom correspond à la variable \verb|file| et le passe en argument à
1042la boucle \verb|while ... done|.
1043
1044\paragraph{Commentaire du \cref{lst:countlines}}
1045\begin{enumerate}
1046\item À la ligne~6, on crée une variable \verb|numline| à laquelle
1047 on attribue la valeur \verb|0|. Elle servira donc de compteur.
1048\item L'instruction essentielle est à la ligne~13:
1049 \mintinline{bash}|while read -r line|: comme ce qui suit
1050 \verb|while| \emph{est un test}, ce test donnera un résultat positif
1051 aussi longtemps que la variable \verb|line| existe, c'est-à-dire
1052 aussi longtemps que l'opérateur de redirection \mintinline{bash}|<|
1053 de la ligne~17 envoie des lignes terminées par un retour
1054 charriot. Tant que cette condition est remplie, la commande de la
1055 ligne~16 sera exécutée, et le compteur de lignes sera incrémenté
1056 d'une unité à chaque fois.
1057\item Les instructions des lignes~20--25 sont faciles à
1058 suivre. Étudiez-les à l'aide du \vref{tab:tests}: vous pourrez
1059 comprendre comment la programmation peut être adaptée pour suivre
1060 les règles de l'orthographe française.
1061\end{enumerate}
1062
1063Exécutons maintenant le script:
1064\begin{minted}{text}
1065[robert@kiddo courses]$ ./countlines.sh
1066Entrez le nom du fichier dont vous voulez compter les lignes:
1067Fichier: whack
1068Erreur: le fichier whack n'existe pas.
1069[robert@kiddo courses]$ ./countlines.sh
1070Entrez le nom du fichier dont vous voulez compter les lignes:
1071Fichier: makefile
1072Votre fichier makefile compte 21 lignes.
1073\end{minted}
1074
1075\paragraph{until}
1076\commande*{until}À la différence de \verb|while|,
1077\verb|until| exécute les instructions qui suivent jusqu'au moment où
1078le résultat du test associé à \verb|until| devient \emph{positif}
1079(\emph{true}). Pour prendre un exemple très simple, le script suivant,
1080que l'on appellera \verb|rah.sh|, demande combien de fois on souhaite
1081tirer la queue d'un lion:
1082\captionof{listing}{bash: comment faire rugir le lion?}
1083\inputfile[linenos]{bash}{rah.sh}
1084
1085\begin{quoting}
1086 \textbf{Commentaire:}
1087 \begin{enumerate}
1088 \item La condition posée à la ligne~17 se comprend ainsi:
1089 \enquote{faites ce qui suit jusqu'à ce que le compteur de
1090 rugissements atteigne une valeur supérieure à celle définie par
1091 l'utilisateur. Si la valeur est supérieure, sortez de la boucle.}
1092\item À la ligne~20, on demande à \emph{bash} d'attendre une seconde.
1093\item À la ligne~21, on incrémente de 1 la valeur du compteur de
1094 rugissements (voir ci-dessus le \vref{lst:countlines}, l.~14), puis
1095 on reprend la boucle à la ligne~17.
1096 \end{enumerate}
1097\end{quoting}
1098
1099Exécution du script \verb|rah.sh|:
1100\begin{minted}{text}
1101[robert@kiddo courses]$ ./rah.sh
1102Combien de fois tirez-vous la queue du lion? 3
1103Raaaaaaaaahhhhhhhhhhhh! (1)
1104Raaaaaaaaahhhhhhhhhhhh! (2)
1105Raaaaaaaaahhhhhhhhhhhh! (3)
1106[robert@kiddo courses]$
1107\end{minted}
1108
1109\paragraph{break}
1110\commande*{break} \verb|break| est une instruction qui
1111ordonne de quitter immédiatement la boucle dans laquelle on se
1112trouve. Supposons par exemple que l'on écrive un programme dans lequel
1113on souhaite limiter une action telle que la copie de
1114fichiers. L'instruction \verb|break| sera exécutée dès que la limite
1115est atteinte. Appelons ce script \verb|copyten.sh|:
1116\captionof{listing}{bash: copie d'un nombre limité de
1117 fichiers\label{lst:copyten}}
1118\inputfile[linenos]{bash}{copyten.sh}
1119
1120Ce script donne un exemple de code commenté. Comme on le voit, les
1121commentaires peuvent se trouver sur des lignes isolées aussi bien que
1122sur des lignes qui contiennent des commandes\footnote{Voir ci-dessus,
1123 \vref{sec:les-commentaires}.}.
1124
1125Exécution du script \verb|copyten.sh|:
1126\begin{minted}{text}
1127[robert@kiddo courses]$ ls *.txt
112801.txt 03.txt 05.txt 07.txt 09.txt 11.txt
112902.txt 04.txt 06.txt 08.txt 10.txt 12.txt
1130[robert@kiddo courses]$ ./copyten.sh
1131Attention: ce programme copie au maximum 10 fichiers.
1132Que souhaitez-vous copier: *.txt
1133Répertoire de destination: Houba
1134Erreur: la destination doit être un répertoire.
1135 Le cas échéant, utilisez "mkdir Houba"
1136 pour créer le répertoire de destination.
1137[robert@kiddo courses]$ mkdir Houba
1138[robert@kiddo courses]$ ./copyten.sh
1139Attention: ce programme copie au maximum 10 fichiers.
1140Que souhaitez-vous copier: *.txt
1141Répertoire de destination: Houba
1142Terminé. 10 fichiers au maximum ont été copiés dans Houba.
1143[robert@kiddo courses]$ ls Houba/
114401.txt 02.txt 03.txt 04.txt 05.txt 06.txt 07.txt
114508.txt 09.txt 10.txt
1146\end{minted}
1147
1148\paragraph{continue}
1149\commande*{continue} À l'inverse de \verb|break|,
1150\verb|continue| demande à \emph{bash} d'interrompre l'itération
1151courante mais sans quitter la boucle, puis de reprendre la boucle à
1152partir de l'itération suivante. C'est une façon de prévoir des
1153exceptions. Par exemple, nous pouvons modifier le script
1154\ref{lst:copyten} comme suit:
1155
1156\captionof{listing}{bash: copie d'un nombre limité de
1157 fichiers sans échec si le fichier source n'existe pas}
1158\inputfile[linenos,highlightlines={26-31}]{bash}{copyten-mk2.sh}
1159
1160\begin{quoting}
1161 \textbf{Commentaire:}
1162 \begin{enumerate}
1163 \item À la ligne~30, l'instruction \verb|continue| intervient dans
1164 le cas où le fichier à copier n'existe pas (voir le test de la
1165 ligne~26).
1166 \item Dans ce cas, le fichier est créé par la ligne~28.
1167 \item Puis \verb|continue| interrompt la boucle et la reprend depuis
1168 la ligne~24.
1169 \end{enumerate}
1170\end{quoting}
1171
1172Voici ce que donne l'exécution de ce script:
1173\begin{minted}{text}
1174[robert@kiddo courses]$ ./copyten-mk2.sh
1175Attention: ce programme copie au maximum 10 fichiers.
1176Que souhaitez-vous copier: tchic.txt
1177Répertoire de destination: Houba
1178création de tchic.txt qui n'existe pas...
1179Terminé. 10 fichiers au maximum ont été copiés dans Houba.
1180[robert@kiddo courses]$ ls Houba/
1181tchic.txt
1182\end{minted}
1183
1184\section{Les fonctions}
1185\label{sec:les-fonctions}
1186Comme leur nom l'indique, les fonctions permettent d'exécuter des
1187instructions de façon autonome dans un script. Elles peuvent être
1188ensuite utilisées aussi souvent que nécessaire et permettent donc de
1189réduire la taille du script. Deux formats sont permis:
1190\begin{minted}[linenos]{bash}
1191# Format 1
1192ma_fonction () {
1193 <commandes>
1194}
1195
1196# Format 2
1197function ma_fonction {
1198<commands>
1199}
1200\end{minted}
1201
1202Ces deux formats sont strictement équivalents. Le premier est le plus
1203répandu car il rappelle ce qui se fait dans de nombreux autres
1204langages où l'on utilise les parenthèses pour passer aux fonctions des
1205variables, des chaînes de caractères, des paramètres optionnels ou
1206même d'autres fonctions. Toutefois, en \emph{bash}, on ne met jamais
1207rien entre les parenthèses qui sont purement décoratives.
1208
1209Le script suivant permet de parvenir au même résultat que le script
1210qui a été présenté dans le \vref{lst:countlines}:
1211\captionof{listing}{bash: exemple de
1212 fonction\label{lst:countlines-mk2}}
1213\inputfile[linenos]{bash}{countlines-mk2.sh}
1214
1215Pour terminer, exécutons cette nouvelle version de notre script
1216ici appelée \verb|countlines-mk2.sh|:
1217
1218\begin{minted}{text}
1219[robert@kiddo courses]$ ./countlines-mk2.sh
1220Entrez le nom du fichier dont vous voulez compter les lignes:
1221Fichier: makefile
1222Votre fichier makefile compte 21 lignes.
1223\end{minted}
1224
1225\printindex[cmds]
1226\end{document}