04-grep-bash.tex 48 KB

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