diff options
Diffstat (limited to 'fichiers/03-grep-bash.tex')
-rw-r--r-- | fichiers/03-grep-bash.tex | 1226 |
1 files changed, 0 insertions, 1226 deletions
diff --git a/fichiers/03-grep-bash.tex b/fichiers/03-grep-bash.tex deleted file mode 100644 index 48ea55f..0000000 --- a/fichiers/03-grep-bash.tex +++ /dev/null | |||
@@ -1,1226 +0,0 @@ | |||
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} | ||
42 | Les 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 | ||
46 | langages de programmation qui permet de construire des | ||
47 | \enquote{modèles}, en anglais \emph{patterns}, susceptibles de | ||
48 | capturer des chaînes de caractères. | ||
49 | |||
50 | Par exemple, soit le fichier suivant: | ||
51 | \begin{minted}{text} | ||
52 | /usr/share/dict/cracklib-small | ||
53 | \end{minted} | ||
54 | Ce fichier fait partie d'un programme dont le rôle est de vérifier la | ||
55 | robustesse des mots de passe. Il contient un grand nombre d'entrées, à | ||
56 | raison d'un mot par ligne. Vérifions cela: | ||
57 | \begin{minted}{text} | ||
58 | [robert@kiddo courses]$ wc -l /usr/share/dict/cracklib-small | ||
59 | 54763 /usr/share/dict/cracklib-small | ||
60 | \end{minted} | ||
61 | L'expression régulière suivante retourne tous les mots de cinq lettres | ||
62 | de ce fichier qui commencent par la lettre \verb|c| et se terminent | ||
63 | par la lettre \verb|h|: | ||
64 | \begin{minted}{text} | ||
65 | [robert@kiddo courses]$ grep '\<c...h\>' /usr/share/dict/cracklib-small | ||
66 | catch | ||
67 | cinch | ||
68 | clash | ||
69 | cloth | ||
70 | coach | ||
71 | conch | ||
72 | couch | ||
73 | cough | ||
74 | crash | ||
75 | crush | ||
76 | czech | ||
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} | ||
86 | Pour construire les modèles (\emph{patterns}), on peut utiliser les | ||
87 | symboles 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 | ||
131 | sous \verb|grep| les metacaractères | ||
132 | \begin{minted}{text} | ||
133 | ? + { } | ( ) | ||
134 | \end{minted} | ||
135 | doivent être précédés de la \emph{séquence d'échappement} \verb|\| | ||
136 | comme ceci: | ||
137 | \begin{minted}{text} | ||
138 | \? \+ \{ \} \| \( \) | ||
139 | \end{minted} | ||
140 | tandis que cela ne se fait pas avec \verb|egrep|. | ||
141 | |||
142 | \paragraph{options} | ||
143 | La commande \verb|(e)grep| peut recevoir un grand nombre | ||
144 | d'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} | ||
158 | Les 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' | ||
163 | scream | ||
164 | seagram | ||
165 | sealteam | ||
166 | seam | ||
167 | sidearm | ||
168 | steam | ||
169 | stream | ||
170 | sunbeam | ||
171 | sunbeam'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 | ||
187 | afoot fleet needn't skeet steep taboo three tweed | ||
188 | beets foote roost sleet steer taboo's three's | ||
189 | boost greet roots sooth stood teems tools | ||
190 | booth hoots scoot steed stool teens tooth | ||
191 | boots loots sheet steel stoop teeth trees | ||
192 | booty 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} | ||
212 | Comme on l'a vu, \verb|bash| est le \emph{shell} le plus répandu sur | ||
213 | les systèmes Linux aujourd'hui. On peut écrire en \verb|bash| des | ||
214 | \emph{scripts}, autrement dit de petits programmes informatiques, pour | ||
215 | réaliser des suites d'opérations plus ou moins complexes. | ||
216 | |||
217 | Voici un exemple très simple. Prenons les lignes suivantes: | ||
218 | \begin{minted}[linenos]{text} | ||
219 | mkdir sauvegarde | ||
220 | cp *.tex sauvegarde | ||
221 | zip -r sauvegarde.zip sauvegarde | ||
222 | \end{minted} | ||
223 | Ces 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 | |||
231 | Pour éviter de répéter ces trois lignes de commande et d'encourir le | ||
232 | risque de se tromper dans la saisie, on peut décider de les écrire | ||
233 | dans un fichier texte que l'on appellera par exemple \verb|backup.sh| | ||
234 | de la façon suivante: | ||
235 | \begin{minted}[linenos]{bash} | ||
236 | #!/bin/bash | ||
237 | mkdir sauvegarde | ||
238 | cp *.tex sauvegarde | ||
239 | zip -r sauvegarde.zip sauvegarde | ||
240 | \end{minted} | ||
241 | |||
242 | Il suffit alors de demander à \verb|bash| d'exécuter ce fichier pour | ||
243 | que 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} | ||
251 | C'est dans un \emph{éditeur de texte} que l'on saisit tout code | ||
252 | informatique. Certains éditeurs de texte sont très simples à | ||
253 | utiliser. 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 | ||
255 | commande: \mintinline{text}|nano|. Après avoir lancé \verb|nano| et | ||
256 | saisi 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 | ||
261 | mkdir sauvegarde | ||
262 | cp *.tex sauvegarde | ||
263 | zip -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 | |||
285 | Les lignes 24 et 25 correspondent au menu de \verb|nano|. On n'y | ||
286 | accède pas par la souris, mais à l'aide des \emph{raccourcis clavier} | ||
287 | qui sont tous préfixés par le \emph{caret} (\verb|^|) qui représente | ||
288 | la touche \keys{Ctrl} du clavier. Donc pour quitter le programme, on | ||
289 | appuiera sur \keys{Ctrl-X}: voici ce que montre \verb|nano| au bas du | ||
290 | terminal 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} | ||
296 | Les 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} | ||
302 | Appuyons sur la touche \keys{O}. \verb|nano| nous invite alors à | ||
303 | entrer le nom du script: | ||
304 | \begin{minted}[linenos,fontsize=\footnotesize]{text} | ||
305 | Nom 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} | ||
309 | Aprè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 | ||
311 | ligne de commande. | ||
312 | |||
313 | \section{Le \emph{shebang}} | ||
314 | \label{sec:le-shebang} | ||
315 | La première ligne du script \verb|backup.sh| donné en exemple | ||
316 | ci-dessus appelle un commentaire particulier: | ||
317 | \begin{minted}[linenos]{bash} | ||
318 | #!/bin/bash | ||
319 | \end{minted} | ||
320 | |||
321 | Dans cette ligne, la séquence \mintinline{bash}|#!| s'appelle le | ||
322 | \emph{shebang}. Par convention, le \emph{shebang} est un préfixe que | ||
323 | l'on fait suivre du nom du programme qui doit interpréter le script, | ||
324 | précédé de son chemin d'accès absolu. | ||
325 | |||
326 | Le \emph{shebang} est important car il permet d'accéder aux | ||
327 | interpréteurs auxquels on souhaite accéder depuis la ligne de | ||
328 | commande. Par exemple, pour un script écrit en Python2, la première | ||
329 | ligne sera: | ||
330 | \begin{minted}[linenos]{python} | ||
331 | #!/usr/bin/env python2 | ||
332 | """ | ||
333 | Mon premier script en Python | ||
334 | """ | ||
335 | print("bonjour le monde!") | ||
336 | \end{minted} | ||
337 | |||
338 | \section{Les commentaires} | ||
339 | \label{sec:les-commentaires} | ||
340 | En \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 | ||
342 | utilise donc ce signe pour introduire des \emph{commentaires} dans le | ||
343 | code informatique. | ||
344 | |||
345 | Les commentaires ne servent pas uniquement à introduire des remarques | ||
346 | pour son usage personnel. Ils servent aussi, et surtout, à donner des | ||
347 | indications sur le code lui-même pour permettre aux autres de mieux | ||
348 | comprendre la programmation. Si le code est bien commenté, alors on | ||
349 | doit pouvoir le lire comme on lit un livre. Cela est très important | ||
350 | car les programmes longs et complexes dépassent souvent et parfois | ||
351 | même survivent à leur auteur. | ||
352 | |||
353 | Si le code est bien compris, il sera facilement mis à jour, corrigé et | ||
354 | augmenté par d'autres programmeurs. | ||
355 | |||
356 | L'art de commenter le code informatique tout en l'écrivant porte le | ||
357 | nom de \emph{literate programming}. Il a été inventé par Donald | ||
358 | E.~Knuth, le créateur de \TeX, qui a posé tous les principes de cet | ||
359 | art dans le cadre de la programmation en | ||
360 | \textsf{WEB}\footnote{\cite{Knuth1983}. Voir également en ligne | ||
361 | \url{http://www.literateprogramming.com/}}. | ||
362 | |||
363 | Pour un exemple de code commenté, voir le \vref{lst:copyten}. | ||
364 | |||
365 | \section{Exécution} | ||
366 | \label{sec:execution} | ||
367 | Il faut ici approfondir la notion de \emph{permissions} sur les | ||
368 | fichiers 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 | ||
371 | sur les fichiers: en lecture, en écriture et en exécution. Revenons | ||
372 | sur 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} | ||
377 | Soit: | ||
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} | ||
386 | La commande qui permet de changer les droits s'appelle | ||
387 | \verb|chmod|. Pour comprendre comment l'utiliser, il faut savoir que | ||
388 | les 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} | ||
394 | Ces valeurs peuvent être additionnées. On analyse donc ainsi les | ||
395 | permissions 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} | ||
401 | Soit $644$. Pour ajouter à l'utilisateur \verb|robert| seulement la | ||
402 | permission en exécution, il faudrait donc porter cette valeur à | ||
403 | $744$. Nous allons ici donner ce droit à la fois à \verb|robert|, au | ||
404 | groupe \verb|robert| et au reste du monde, soit une valeur de | ||
405 | $755$. La syntaxe est la suivante: | ||
406 | \begin{minted}{text} | ||
407 | chmod xyz <fichier> | ||
408 | \end{minted} | ||
409 | où \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 | |||
428 | Nous pouvons désormais exécuter notre script: | ||
429 | \begin{minted}[linenos,escapeinside=||]{text} | ||
430 | [robert@kiddo courses]$ ls -l --color | ||
431 | total 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 | ||
434 | drwxr-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 | ||
448 | total 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 | ||
451 | drwxr-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 | ||
458 | drwxr-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, | ||
481 | pourquoi a-t-on écrit \mintinline{bash}|./backup.sh| et non pas | ||
482 | simplement \mintinline{bash}|backup.sh|? Tout simplement pour des | ||
483 | raisons de sécurité. En effet, le principe est que les fichiers | ||
484 | exécutables se trouvent dans certains répertoires-système spécialement | ||
485 | conçus pour les accueillir. C'est pour cette raison que l'on peut | ||
486 | lancer les commandes \verb|bash| sans avoir à les préfixer. Or notre | ||
487 | répertoire de travail ne fait partie de ces répertoires spéciaux. Il | ||
488 | faut 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 | ||
490 | première méthode: dans la séquence \mintinline{text}|./|, le point | ||
491 | représente le répertoire courant tandis que le \emph{slash} précise | ||
492 | qu'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 | ||
494 | caché. | ||
495 | |||
496 | \section{Les variables} | ||
497 | \label{sec:les-variables} | ||
498 | Les variables sont des informations temporaires qui peuvent être | ||
499 | stockées et rappelées à tout moment. L'exemple qui suit va donner | ||
500 | l'occasion d'étudier une nouvelle commande, | ||
501 | \commande{echo}\verb|echo|, dont le rôle est justement de retourner | ||
502 | au 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.' | ||
506 | Mon système est $mysystem. | ||
507 | [robert@kiddo courses]$ echo "Mon système est $mysystem." | ||
508 | Mon 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 | |||
533 | Certaines variables sont automatiquement définies par \emph{bash}. Par | ||
534 | exemple: | ||
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 | |||
541 | Nous pouvons désormais perfectionner le script \verb|backup.sh|: | ||
542 | \begin{minted}[linenos]{bash} | ||
543 | #!/bin/bash | ||
544 | echo "Veuillez choisir l'extension des fichiers à sauvegarder" | ||
545 | echo "(sans le point):" | ||
546 | read -p 'extension: ' ext | ||
547 | echo "Veuillez choisir le nom du dossier de sauvegarde:" | ||
548 | read -p 'dossier: ' backupdir | ||
549 | mkdir "$backupdir" | ||
550 | cp *.$ext "$backupdir" | ||
551 | zip -r "$backupdir".zip "$backupdir" | ||
552 | echo "Terminé. $0 a copié vos fichiers .$ext dans $backupdir" | ||
553 | echo "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 | ||
559 | le 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 | |||
579 | Exécution du script \verb|./backup.sh tex|: | ||
580 | \begin{minted}[linenos]{text} | ||
581 | [robert@kiddo courses]$ ./backup.sh | ||
582 | Veuillez choisir l'extension des fichiers à sauvegarder | ||
583 | (sans le point): | ||
584 | extension: tex | ||
585 | Veuillez choisir le nom du dossier de sauvegarde: | ||
586 | dossier: 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%) | ||
592 | Terminé. ./backup.sh a copié vos fichiers .tex dans Houba | ||
593 | et 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} | ||
607 | Cette technique simple permet de transformer en variable le résultat | ||
608 | d'une commande. Il suffit de placer la commande entre parenthèses | ||
609 | pré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 | ||
613 | 8 | ||
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 | |||
628 | Utilisons cette technique pour perfectionner notre script | ||
629 | \verb|backup.sh| (voir ci-dessous la ligne~9): | ||
630 | \begin{minted}[linenos]{bash} | ||
631 | #!/bin/bash | ||
632 | echo "Veuillez choisir l'extension des fichiers à sauvegarder" | ||
633 | echo "(sans le point):" | ||
634 | read -p 'extension: ' ext | ||
635 | echo "Veuillez choisir le nom du dossier de sauvegarde:" | ||
636 | read -p 'dossier: ' backupdir | ||
637 | mkdir "$backupdir" | ||
638 | cp *.$ext "$backupdir" | ||
639 | nbre=$(ls $backupdir/*.$ext | wc -l) | ||
640 | zip -r "$backupdir".zip "$backupdir" | ||
641 | echo "Terminé. $0 a copié $nbre fichiers .$ext dans $backupdir" | ||
642 | echo "et l'archive $backupdir.zip a été créée." | ||
643 | \end{minted} | ||
644 | |||
645 | Exécution: | ||
646 | \begin{minted}[linenos]{text} | ||
647 | [robert@kiddo courses]$ ./backup.sh | ||
648 | Veuillez choisir l'extension des fichiers à sauvegarder | ||
649 | (sans le point): | ||
650 | extension: tex | ||
651 | Veuillez choisir le nom du dossier de sauvegarde: | ||
652 | dossier: 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%) | ||
658 | Terminé. ./backup.sh a copié 4 fichiers .tex dans Houba | ||
659 | et l'archive Houba.zip a été créée. | ||
660 | \end{minted} | ||
661 | |||
662 | \section{Conditions} | ||
663 | \label{sec:conditions} | ||
664 | Il est souvent très utile de n'exécuter des lignes de code que si | ||
665 | certaines conditions sont remplies. Reprenons ici l'exemple du script | ||
666 | \verb|backup.sh|: au moment où le script crée le répertoire de | ||
667 | sauvegarde, il ne contrôle pas si ce répertoire existe déjà, ce qui est | ||
668 | un défaut. Voici comment on peut créer une condition adaptée à cette | ||
669 | situation: | ||
670 | \begin{minted}[linenos]{bash} | ||
671 | if [ -d "$backupdir" ] | ||
672 | then | ||
673 | echo "Le dossier $backupdir existe déjà. Veuillez relancer le" | ||
674 | echo "programme et saisir un autre nom." | ||
675 | exit 1 | ||
676 | else | ||
677 | mkdir "$backupdir" | ||
678 | fi | ||
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 | |||
706 | Voici donc comment se présente le script \verb|backup.sh| une fois la | ||
707 | condition 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} | ||
714 | Supposons que le répertoire de sauvegarde ait été supprimé mais que | ||
715 | l'archive \verb|.zip| correspondante ne l'ait pas été. Dans ce cas, le | ||
716 | script \verb|backup.sh| l'écraserait. Pour éviter cela, nous pouvons | ||
717 | utiliser dans le script l'instruction | ||
718 | \commande*{elif}\verb|elif| qui permet de construire des | ||
719 | conditions en série. Littéralement, \verb|elif| est pour \emph{else | ||
720 | if}, \enquote{ou autrement, si\ldots}. Voici donc ce qu'il faut | ||
721 | ajouter: | ||
722 | \begin{minted}{bash} | ||
723 | elif [ -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 | |||
731 | Ce 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} | ||
737 | Le tableau suivant donne la liste des tests les plus répandus que l'on | ||
738 | peut associer aux conditions. Les tests sont placés entre crochets | ||
739 | droits 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 | ||
772 | crochets 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 | ||
776 | est associé à une variable | ||
777 | \verb|$?| que l'on peut afficher par la commande: | ||
778 | \commande{echo}\mintinline{bash}|echo $?|. En voici quelques | ||
779 | exemples: | ||
780 | \begin{minted}[linenos]{text} | ||
781 | [robert@kiddo courses]$ ls | ||
782 | bibliography.bib makefile _preamble-ed.tex _preamble.tex README.tex | ||
783 | fichiers _preamble_bbl.tex _preamble.log README.md | ||
784 | [robert@kiddo courses]$ test -d fichiers | ||
785 | [robert@kiddo courses]$ echo $? | ||
786 | 0 | ||
787 | [robert@kiddo courses]$ test -x makefile | ||
788 | [robert@kiddo courses]$ echo $? | ||
789 | 1 | ||
790 | [robert@kiddo courses]$ test -e makefile | ||
791 | [robert@kiddo courses]$ echo $? | ||
792 | 0 | ||
793 | [robert@kiddo courses]$ test 0123 = 123 | ||
794 | [robert@kiddo courses]$ echo $? | ||
795 | 1 | ||
796 | [robert@kiddo courses]$ test 0123 -eq 123 | ||
797 | [robert@kiddo courses]$ echo $? | ||
798 | 0 | ||
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} | ||
809 | L'indentation est une technique qui consiste à donner aux lignes de | ||
810 | code différentes profondeurs de marge à gauche de façon à distinguer | ||
811 | clairement des blocs logiques. Les lignes 7--20 du | ||
812 | \vref{lst:if-then-else} en donnent un exemple. L'indentation permet de | ||
813 | faire 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 | |||
817 | Cette technique est commune à tous les langages informatiques et tous | ||
818 | les programmeurs l'utilisent. En Python, que nous étudierons plus | ||
819 | tard, l'indendation fait même partie du langage lui-même, puisque | ||
820 | Python a recours à l'indentation pour structurer les blocs logiques, à | ||
821 | la différence d'autres langages qui ont recours soit à des | ||
822 | déclarations explicites telles que \verb|begin ... end| ou encore à | ||
823 | des opérateurs tels que \verb|{ ... }| | ||
824 | |||
825 | Voici un exemple d'indentation d'une structure dans laquelle on a | ||
826 | placé 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} | ||
832 | Les opérateurs booléens, dont le nom est tiré du mathématicien anglais | ||
833 | George Boole, sont des opérateurs logiques que l'on peut associer à | ||
834 | des conditions. En voici deux ici: | ||
835 | \begin{enumerate} | ||
836 | \item \verb+||+ pour \emph{ou}; | ||
837 | \item \verb|&&| pour \emph{et}. | ||
838 | \end{enumerate} | ||
839 | |||
840 | Par exemple, dans le \vref{lst:elif} ci-dessus, on pourrait remplacer | ||
841 | les lignes 7--20 par le code ci-dessous: | ||
842 | \begin{minted}[linenos]{bash} | ||
843 | if [ -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 | ||
849 | else | ||
850 | mkdir "$backupdir" | ||
851 | fi | ||
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 | ||
857 | actions 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 | ||
859 | aux expressions régulières. La syntaxe est la suivante: | ||
860 | \begin{minted}[linenos]{bash} | ||
861 | case $var in | ||
862 | expr1) | ||
863 | <commandes ...> | ||
864 | ;; | ||
865 | expr2) | ||
866 | <commandes ...> | ||
867 | ;; | ||
868 | esac | ||
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 | |||
883 | Le \vref{lst:case} montre un exemple facile à comprendre de cette | ||
884 | technique. | ||
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} | ||
914 | Les boucles (en anglais: \emph{loops}) servent à indiquer qu'une série | ||
915 | d'instructions doit reprendre et continuer à s'exécuter aussi | ||
916 | longtemps qu'une condition donnée est remplie ou n'est pas remplie. | ||
917 | |||
918 | Prenons un exemple simple. Nous avons dans un répertoire plusieurs | ||
919 | centaines d'images de différents formats et nous souhaitons convertir | ||
920 | au format \verb|.png| toutes les images qui sont enregistrées au | ||
921 | format \verb|.tiff|. | ||
922 | |||
923 | Pour convertir une seule image, nous pouvons utiliser l'outil en ligne | ||
924 | de commande \verb|convert| fourni par le programme | ||
925 | \emph{imagemagick}\footnote{\url{http://www.imagemagick.org}}. Pour | ||
926 | convertir une seule image, la syntaxe est la suivante: | ||
927 | \begin{minted}{bash} | ||
928 | convert image.tiff image.png | ||
929 | \end{minted} | ||
930 | |||
931 | Mais comment faire pour en convertir un grand nombre pris dans un | ||
932 | répertoire qui en compte des centaines enregistrées dans des formats | ||
933 | différents? | ||
934 | |||
935 | \paragraph{basename} | ||
936 | \commande*{basename}Avant de continuer, il faut dire un mot de la | ||
937 | commande \verb|basename| que nous allons utiliser ici. Cette commande | ||
938 | permet de dépouiller un nom de fichier de son chemin d'accès et de son | ||
939 | extension. La syntaxe est la suivante: | ||
940 | \begin{minted}[escapeinside=||]{text} | ||
941 | basename -s .|\emph{ext}| |\emph{file}| | ||
942 | \end{minted} | ||
943 | où \emph{ext} est le nom de l'extension et \emph{file} le nom du | ||
944 | fichier. Voici un exemple: | ||
945 | \begin{minted}{text} | ||
946 | [robert@kiddo fichiers]$ ls -l images/ | ||
947 | total 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/* | ||
954 | 02-ascii | ||
955 | 02-donnees1 | ||
956 | 02-donnees2 | ||
957 | 02-exercice_formats | ||
958 | 02-unicode | ||
959 | \end{minted} | ||
960 | |||
961 | \paragraph{for-do-done} | ||
962 | \commande*{for}\commande*{do}\commande*{done} | ||
963 | La 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 | |||
976 | Voici 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 | ||
1006 | lignes de code qui suivent aussi longtemps qu'un test donné retourne | ||
1007 | un résultat positif (\enquote{vrai}, en anglais \emph{true}). Le | ||
1008 | script \verb|countlines.sh|, donné dans le \cref{lst:countlines} | ||
1009 | ci-dessous, utilise cette expression pour compter les lignes des | ||
1010 | fichiers\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} | ||
1017 | On a déjà présenté plus haut \vpageref{ref:read} cette | ||
1018 | instruction. Pour bien comprendre le \cref{lst:countlines}, il faut | ||
1019 | savoir que la fonction de \verb|read| est de \emph{lire une ligne}, | ||
1020 | quelle 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 | ||
1022 | en question était donc constituée d'une saisie au clavier dans | ||
1023 | laquelle la ligne était formée par une chaîne de caractères terminée | ||
1024 | par 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}: | ||
1037 | l'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 | ||
1041 | le nom correspond à la variable \verb|file| et le passe en argument à | ||
1042 | la 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 | |||
1063 | Exécutons maintenant le script: | ||
1064 | \begin{minted}{text} | ||
1065 | [robert@kiddo courses]$ ./countlines.sh | ||
1066 | Entrez le nom du fichier dont vous voulez compter les lignes: | ||
1067 | Fichier: whack | ||
1068 | Erreur: le fichier whack n'existe pas. | ||
1069 | [robert@kiddo courses]$ ./countlines.sh | ||
1070 | Entrez le nom du fichier dont vous voulez compter les lignes: | ||
1071 | Fichier: makefile | ||
1072 | Votre 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ù | ||
1078 | le résultat du test associé à \verb|until| devient \emph{positif} | ||
1079 | (\emph{true}). Pour prendre un exemple très simple, le script suivant, | ||
1080 | que l'on appellera \verb|rah.sh|, demande combien de fois on souhaite | ||
1081 | tirer 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 | |||
1099 | Exécution du script \verb|rah.sh|: | ||
1100 | \begin{minted}{text} | ||
1101 | [robert@kiddo courses]$ ./rah.sh | ||
1102 | Combien de fois tirez-vous la queue du lion? 3 | ||
1103 | Raaaaaaaaahhhhhhhhhhhh! (1) | ||
1104 | Raaaaaaaaahhhhhhhhhhhh! (2) | ||
1105 | Raaaaaaaaahhhhhhhhhhhh! (3) | ||
1106 | [robert@kiddo courses]$ | ||
1107 | \end{minted} | ||
1108 | |||
1109 | \paragraph{break} | ||
1110 | \commande*{break} \verb|break| est une instruction qui | ||
1111 | ordonne de quitter immédiatement la boucle dans laquelle on se | ||
1112 | trouve. Supposons par exemple que l'on écrive un programme dans lequel | ||
1113 | on souhaite limiter une action telle que la copie de | ||
1114 | fichiers. L'instruction \verb|break| sera exécutée dès que la limite | ||
1115 | est 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 | |||
1120 | Ce script donne un exemple de code commenté. Comme on le voit, les | ||
1121 | commentaires peuvent se trouver sur des lignes isolées aussi bien que | ||
1122 | sur des lignes qui contiennent des commandes\footnote{Voir ci-dessus, | ||
1123 | \vref{sec:les-commentaires}.}. | ||
1124 | |||
1125 | Exécution du script \verb|copyten.sh|: | ||
1126 | \begin{minted}{text} | ||
1127 | [robert@kiddo courses]$ ls *.txt | ||
1128 | 01.txt 03.txt 05.txt 07.txt 09.txt 11.txt | ||
1129 | 02.txt 04.txt 06.txt 08.txt 10.txt 12.txt | ||
1130 | [robert@kiddo courses]$ ./copyten.sh | ||
1131 | Attention: ce programme copie au maximum 10 fichiers. | ||
1132 | Que souhaitez-vous copier: *.txt | ||
1133 | Répertoire de destination: Houba | ||
1134 | Erreur: 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 | ||
1139 | Attention: ce programme copie au maximum 10 fichiers. | ||
1140 | Que souhaitez-vous copier: *.txt | ||
1141 | Répertoire de destination: Houba | ||
1142 | Terminé. 10 fichiers au maximum ont été copiés dans Houba. | ||
1143 | [robert@kiddo courses]$ ls Houba/ | ||
1144 | 01.txt 02.txt 03.txt 04.txt 05.txt 06.txt 07.txt | ||
1145 | 08.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 | ||
1151 | courante mais sans quitter la boucle, puis de reprendre la boucle à | ||
1152 | partir de l'itération suivante. C'est une façon de prévoir des | ||
1153 | exceptions. 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 | |||
1172 | Voici ce que donne l'exécution de ce script: | ||
1173 | \begin{minted}{text} | ||
1174 | [robert@kiddo courses]$ ./copyten-mk2.sh | ||
1175 | Attention: ce programme copie au maximum 10 fichiers. | ||
1176 | Que souhaitez-vous copier: tchic.txt | ||
1177 | Répertoire de destination: Houba | ||
1178 | création de tchic.txt qui n'existe pas... | ||
1179 | Terminé. 10 fichiers au maximum ont été copiés dans Houba. | ||
1180 | [robert@kiddo courses]$ ls Houba/ | ||
1181 | tchic.txt | ||
1182 | \end{minted} | ||
1183 | |||
1184 | \section{Les fonctions} | ||
1185 | \label{sec:les-fonctions} | ||
1186 | Comme leur nom l'indique, les fonctions permettent d'exécuter des | ||
1187 | instructions de façon autonome dans un script. Elles peuvent être | ||
1188 | ensuite utilisées aussi souvent que nécessaire et permettent donc de | ||
1189 | réduire la taille du script. Deux formats sont permis: | ||
1190 | \begin{minted}[linenos]{bash} | ||
1191 | # Format 1 | ||
1192 | ma_fonction () { | ||
1193 | <commandes> | ||
1194 | } | ||
1195 | |||
1196 | # Format 2 | ||
1197 | function ma_fonction { | ||
1198 | <commands> | ||
1199 | } | ||
1200 | \end{minted} | ||
1201 | |||
1202 | Ces deux formats sont strictement équivalents. Le premier est le plus | ||
1203 | répandu car il rappelle ce qui se fait dans de nombreux autres | ||
1204 | langages où l'on utilise les parenthèses pour passer aux fonctions des | ||
1205 | variables, des chaînes de caractères, des paramètres optionnels ou | ||
1206 | même d'autres fonctions. Toutefois, en \emph{bash}, on ne met jamais | ||
1207 | rien entre les parenthèses qui sont purement décoratives. | ||
1208 | |||
1209 | Le script suivant permet de parvenir au même résultat que le script | ||
1210 | qui 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 | |||
1215 | Pour terminer, exécutons cette nouvelle version de notre script | ||
1216 | ici appelée \verb|countlines-mk2.sh|: | ||
1217 | |||
1218 | \begin{minted}{text} | ||
1219 | [robert@kiddo courses]$ ./countlines-mk2.sh | ||
1220 | Entrez le nom du fichier dont vous voulez compter les lignes: | ||
1221 | Fichier: makefile | ||
1222 | Votre fichier makefile compte 21 lignes. | ||
1223 | \end{minted} | ||
1224 | |||
1225 | \printindex[cmds] | ||
1226 | \end{document} | ||