123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227 |
- \input{../_preamble}
- \input{../_preamble_bbl}
- \usepackage{menukeys}
- \title{grep et bash}
- \usepackage{float}
- \usepackage{dingbat}
- \usepackage[newfloat]{minted}
- \SetupFloatingEnvironment{listing}{listname=Listings}
- \setminted{
- bgcolor=Lavender,
- breaklines,
- breaksymbolright=\small\carriagereturn}
- \setmintedinline{bgcolor=Lavender}
- \usepackage{capt-of}
- \usepackage{soul}
- \makeindex[name=cmds, intoc, title={Liste des commandes et
- instructions}, options={-s \jobname.ist}]
- \NewDocumentCommand{\commande}{s m O{}}{
- \IfBooleanTF{#1}{\index[cmds]{#2@\texttt{#2}|#3textbf}}
- {\index[cmds]{#2@\texttt{#2}#3}}
- }
- \NewDocumentCommand{\inputfile}{O{} m m O{application/x-sh}}{
- \marginpar{\attachandlink{scripts/#3}[#4]{Fichier
- attaché}{\textcolor{blue}{Ouvrir le fichier}}}
- \inputminted[#1]{#2}{scripts/#3}
- }
- \begin{document}
- \maketitle
- \renewcommand{\contentsname}{Sommaire}
- \tableofcontents
- \listoflistings
- \needspace{3\baselineskip}
- \listoftables
- \chapter{grep, les expressions régulières}
- \label{cha:grep-les-expressions}\commande{grep}
- Les expressions régulières se rapprochent des \emph{wildcards} ou
- \enquote{métacaractères} qui ont été présentés dans
- \href{./01-ligne-de-commande.pdf#lnk_wildcards}{le cours sur la ligne
- de commande}. C'est une technique commune à pour ainsi dire tous les
- langages de programmation qui permet de construire des
- \enquote{modèles}, en anglais \emph{patterns}, susceptibles de
- capturer des chaînes de caractères.
- Par exemple, soit le fichier suivant:
- \begin{minted}{text}
- /usr/share/dict/cracklib-small
- \end{minted}
- Ce fichier fait partie d'un programme dont le rôle est de vérifier la
- robustesse des mots de passe. Il contient un grand nombre d'entrées, à
- raison d'un mot par ligne. Vérifions cela:
- \begin{minted}{text}
- [robert@kiddo courses]$ wc -l /usr/share/dict/cracklib-small
- 54763 /usr/share/dict/cracklib-small
- \end{minted}
- L'expression régulière suivante retourne tous les mots de cinq lettres
- de ce fichier qui commencent par la lettre \verb|c| et se terminent
- par la lettre \verb|h|:
- \begin{minted}{text}
- [robert@kiddo courses]$ grep '\<c...h\>' /usr/share/dict/cracklib-small
- catch
- cinch
- clash
- cloth
- coach
- conch
- couch
- cough
- crash
- crush
- czech
- \end{minted}
- \begin{quoting}
- \textsc{Rem.} \verb|grep| recherche les modèles ligne par
- ligne et retourne donc un résultat positif dès lors qu'un modèle
- donné a été trouvé au moins une fois dans une ligne.
- \end{quoting}
- \paragraph{Modèles}
- Pour construire les modèles (\emph{patterns}), on peut utiliser les
- symboles suivants\footnote{Cette liste n'est pas exhaustive.}:
- \begin{xltabular}{\linewidth}{lX}
- \toprule
- Symbole & Signification \\ \midrule\endhead
- \verb|.| & tout caractère unique\\
- \verb|?| & le caractère précédent est répété 0 ou une fois\\
- \verb|*| & le caractère précédent est répété 0 fois ou autant
- de fois que possible\\
- \verb|+| & le caractère précédent est répété une fois \emph{au
- moins}\\
- \verb|{n}| & le caractère précédent est répété exactement \emph{n}
- fois\\
- \verb|{n,m}| & le caractère précédent est répété au moins \emph{n}
- fois et au plus \emph{m} fois\\
- \verb|[abc]| & le caractère précédent est l'un de ceux qui se
- trouvent entre les crochets droits\\
- \verb|[^abc]| & le caractère précédent n'est pas l'un de ceux qui se
- trouvent entre les crochets droits\\
- \verb|[a-z]| & le caractère précédent est compris entre \emph{a} et
- \emph{z}, dans l'ordre de la table des
- caractères. C'est le sens du trait d'union entre les
- lettres \verb|a| et \verb|z|. On peut bien sûr
- combiner des chaînes avec et sans trait d'union. Par
- exemple, \verb|[A-Ea-e]| correspond aux cinq
- premières lettres de l'alphabet, en majuscule et en
- minuscule. \\
- \verb|()| & ce qui est inclus entre les parenthèses est traité comme
- un groupe \\
-
- \verb+|+ & opérateur logique signifiant \emph{ou} \\
- \verb|^| & représente le début de la ligne\\
- \verb|$| & représente la fin de la ligne\\ \\
- \verb|\<| et \verb|\>| & représentent respectivement un début et une
- fin de mot\\
- \bottomrule
- \caption{grep \emph{patterns}}
- \end{xltabular}
- \paragraph{grep, egrep}
- \commande*{grep}
- \commande{egrep}[|see{\texttt{grep}}]
- À la place de \verb|grep|, on peut saisir à la ligne de commande
- \verb|egrep| ou \verb|grep -E| pour \emph{extended regular
- expressions}. Quelle est la différence? Retenez ici simplement que
- sous \verb|grep| les metacaractères
- \begin{minted}{text}
- ? + { } | ( )
- \end{minted}
- doivent être précédés de la \emph{séquence d'échappement} \verb|\|
- comme ceci:
- \begin{minted}{text}
- \? \+ \{ \} \| \( \)
- \end{minted}
- tandis que cela ne se fait pas avec \verb|egrep|.
- \paragraph{options}
- La commande \verb|(e)grep| peut recevoir un grand nombre
- d'options. Parmi ces options, retenons celles-ci:
- \begin{description}
- \item[-n] retourne les numéros des lignes dans lesquelles le modèle de
- recherche a été trouvé.
- \item[-c] retourne le nombre d'occurrences trouvées.
- \item[-i] demande à \verb|grep| de ne pas faire de différence entre
- les minuscules et les majuscules.
- \item[-H] retourne le nom du fichier dans lequel le modèle recherché
- est trouvé.
- \item[-v] \emph{nie} le modèle recherché: \verb|grep| retournera donc
- les lignes dans lesquelles le modèle \emph{n'a pas été trouvé}.
- \end{description}
- \paragraph{Exemples}
- Les exemples ci-dessous utilisent
- \href{./01-ligne-de-commande.pdf#lnk_redirection}{la technique de la
- redirection}.
- \begin{minted}{text}
- [robert@kiddo courses]$ cat /usr/share/dict/cracklib-small | grep '\<s.*m\>' | grep 'ea'
- scream
- seagram
- sealteam
- seam
- sidearm
- steam
- stream
- sunbeam
- sunbeam's
- \end{minted}
- \begin{quoting}
- \textbf{Commentaire:} La ligne de commande fait ici successivement
- les opérations suivantes:
- \begin{enumerate}
- \item Concaténation de toutes les lignes du fichier
- \verb|cracklib-small|
- \item Sélection de tous les mots qui commencent par la lettre
- \verb|s| et se terminent par la lettre \verb|m|.
- \item Parmi ces mots, sélection de ceux qui contiennent la chaîne
- \verb|ea|.
- \end{enumerate}
- \end{quoting}
- \begin{minted}{text}
- [robert@kiddo courses]$ cat /usr/share/dict/cracklib-small | grep '\<.....\>' | grep -E 'o{2}|e{2}' | grep 't' | column -c 70
- afoot fleet needn't skeet steep taboo three tweed
- beets foote roost sleet steer taboo's three's
- boost greet roots sooth stood teems tools
- booth hoots scoot steed stool teens tooth
- boots loots sheet steel stoop teeth trees
- booty meets shoot steen sweet tepee troop
- \end{minted}
- \begin{quoting}
- \textbf{Commentaire:} La ligne de commande fait ici successivement
- les opérations suivantes:
- \begin{enumerate}
- \item Concaténation de toutes les lignes du fichier
- \verb|cracklib-small|
- \item Sélection des mots de cinq caractères.
- \item Parmi ces mots, sélection de ceux qui contiennent \emph{soit}
- la chaîne \verb|oo| \emph{soit} la chaîne \verb|ee|.
- \item Enfin, sélection, parmi ces derniers mots, de ceux qui
- contiennent la lettre \verb|t|
- \item La dernière ligne, dont on n'a pas étudié la syntaxe, demande
- l'affichage du résultat sous la forme de colonnes tabulées.
- \end{enumerate}
- \end{quoting}
- \chapter{bash}
- \label{cha:bash}
- Comme on l'a vu, \verb|bash| est le \emph{shell} le plus répandu sur
- les systèmes Linux aujourd'hui. On peut écrire en \verb|bash| des
- \emph{scripts}, autrement dit de petits programmes informatiques, pour
- réaliser des suites d'opérations plus ou moins complexes.
- Voici un exemple très simple. Prenons les lignes suivantes:
- \begin{minted}[linenos]{text}
- mkdir sauvegarde
- cp *.tex sauvegarde
- zip -r sauvegarde.zip sauvegarde
- \end{minted}
- Ces trois lignes exécutent successivement les opérations suivantes:
- \begin{enumerate}
- \item Création d'un répertoire intitulé \verb|sauvegarde|
- \item Copie de tous les fichiers \TeX{} dans le répertoire
- \verb|sauvegarde|
- \item Création d'une archive \verb|.zip| de tout le répertoire.
- \end{enumerate}
- Pour éviter de répéter ces trois lignes de commande et d'encourir le
- risque de se tromper dans la saisie, on peut décider de les écrire
- dans un fichier texte que l'on appellera par exemple \verb|backup.sh|
- de la façon suivante:
- \begin{minted}[linenos]{bash}
- #!/bin/bash
- mkdir sauvegarde
- cp *.tex sauvegarde
- zip -r sauvegarde.zip sauvegarde
- \end{minted}
- Il suffit alors de demander à \verb|bash| d'exécuter ce fichier pour
- que les trois opérations soient réalisées d'un coup. Comme les scripts
- écrits en \verb|bash| sont interprétés par le \textsl{shell}
- \verb|bash|, \emph{toute ligne de commande peut être exécutée dans un
- script}. Réciproquement, \emph{tout ce qui peut entrer dans un
- script peut aussi être exécuté à la ligne de commande}.
- \section{L'éditeur de texte}
- \label{sec:lediteur-de-texte}
- C'est dans un \emph{éditeur de texte} que l'on saisit tout code
- informatique. Certains éditeurs de texte sont très simples à
- utiliser. Nous allons prendre ici l'exemple de l'un des plus simples,
- \verb|nano|. Pour le lancer, il suffit de saisir à la ligne de
- commande: \mintinline{text}|nano|. Après avoir lancé \verb|nano| et
- saisi le script donné ci-dessus, voici ce que l'on obtient:
- \begin{minted}[linenos,fontsize=\footnotesize]{text}
- GNU nano 2.8.2 Nouvel espace
- #!/bin/bash
- mkdir sauvegarde
- cp *.tex sauvegarde
- zip -r sauvegarde.zip sauvegarde
- ^G Aide ^O Écrire ^W Chercher ^K Couper ^J Justifier ^C Pos. cur.
- ^X Quitter ^R Lire fich.^\ Remplacer ^U Coller ^T Orthograp.^_ Aller lig.
- \end{minted}
- Les lignes 24 et 25 correspondent au menu de \verb|nano|. On n'y
- accède pas par la souris, mais à l'aide des \emph{raccourcis clavier}
- qui sont tous préfixés par le \emph{caret} (\verb|^|) qui représente
- la touche \keys{Ctrl} du clavier. Donc pour quitter le programme, on
- appuiera sur \keys{Ctrl-X}: voici ce que montre \verb|nano| au bas du
- terminal après avoir saisi \keys{Ctrl-X}:
- \begin{minted}[linenos,fontsize=\footnotesize]{text}
- Écrire l'espace modifié ? (Répondre « Non » ABANDONNE les modifications.)
- O Oui
- N Non ^C Annuler
- \end{minted}
- Les opérations suivantes sont donc possibles:
- \begin{enumerate}
- \item \keys{O}: sauvegarde le fichier.
- \item \keys{N}: quitte \verb|nano| sans sauvegarder.
- \item \keys{Ctrl-C}: annule l'opération et retourne à l'éditeur de texte.
- \end{enumerate}
- Appuyons sur la touche \keys{O}. \verb|nano| nous invite alors à
- entrer le nom du script:
- \begin{minted}[linenos,fontsize=\footnotesize]{text}
- Nom du fichier à écrire: backup.sh
- ^G Aide M-D Format DOS M-A Ajout (à la fin)M-B Copie de sécu.
- ^C Annuler M-M Format Mac M-P Ajout (au début)^T Parcourir
- \end{minted}
- Après avoir entré le nom du fichier et appuyé sur la touche
- \keys{Enter} pour confirmer le choix, on retourne au terminal et à la
- ligne de commande.
- \section{Le \emph{shebang}}
- \label{sec:le-shebang}
- La première ligne du script \verb|backup.sh| donné en exemple
- ci-dessus appelle un commentaire particulier:
- \begin{minted}[linenos]{bash}
- #!/bin/bash
- \end{minted}
- Dans cette ligne, la séquence \mintinline{bash}|#!| s'appelle le
- \emph{shebang}. Par convention, le \emph{shebang} est un préfixe que
- l'on fait suivre du nom du programme qui doit interpréter le script,
- précédé de son chemin d'accès absolu.
- Le \emph{shebang} est important car il permet d'accéder aux
- interpréteurs auxquels on souhaite accéder depuis la ligne de
- commande. Par exemple, pour un script écrit en Python2, la première
- ligne sera:
- \begin{minted}[linenos]{python}
- #!/usr/bin/env python2
- """
- Mon premier script en Python
- """
- print("bonjour le monde!")
- \end{minted}
- \section{Les commentaires}
- \label{sec:les-commentaires}
- En \verb|bash|, tout ce qui, sur une même ligne, suit le signe
- \mintinline{bash}|#| \emph{n'est ni interprété, ni exécuté}. On
- utilise donc ce signe pour introduire des \emph{commentaires} dans le
- code informatique.
- Les commentaires ne servent pas uniquement à introduire des remarques
- pour son usage personnel. Ils servent aussi, et surtout, à donner des
- indications sur le code lui-même pour permettre aux autres de mieux
- comprendre la programmation. Si le code est bien commenté, alors on
- doit pouvoir le lire comme on lit un livre. Cela est très important
- car les programmes longs et complexes dépassent souvent et parfois
- même survivent à leur auteur.
- Si le code est bien compris, il sera facilement mis à jour, corrigé et
- augmenté par d'autres programmeurs.
- L'art de commenter le code informatique tout en l'écrivant porte le
- nom de \emph{literate programming}. Il a été inventé par Donald
- E.~Knuth, le créateur de \TeX, qui a posé tous les principes de cet
- art dans le cadre de la programmation en
- \textsf{WEB}\footnote{\cite{Knuth1983}. Voir également en ligne
- \url{http://www.literateprogramming.com/}}.
- Pour un exemple de code commenté, voir le \vref{lst:copyten}.
- \section{Exécution}
- \label{sec:execution}
- Il faut ici approfondir la notion de \emph{permissions} sur les
- fichiers qui a été présentée dans le cours sur la
- \href{./01-ligne-de-commande.pdf#lnk_permissions}{ligne
- de commande}. Nous avons en effet étudié trois types de permissions
- sur les fichiers: en lecture, en écriture et en exécution. Revenons
- sur les permissions données par défaut au script \verb|backup.sh|:
- \begin{minted}{text}
- [robert@kiddo courses]$ ls -l backup.sh
- -rw-r--r-- 1 robert robert 82 17 sept. 22:06 backup.sh
- \end{minted}
- Soit:
- \begin{itemize}
- \item lecture et écriture pour l'utilisateur \verb|robert| (\verb|rw|);
- \item lecture seule pour le groupe \verb|robert| (\verb|r|);
- \item lecture seule pour le reste du monde (\verb|r|)
- \end{itemize}
- \paragraph{chmod}
- \commande*{chmod}
- La commande qui permet de changer les droits s'appelle
- \verb|chmod|. Pour comprendre comment l'utiliser, il faut savoir que
- les permissions sont traduites par des valeurs numériques, à savoir:
- \begin{itemize}
- \item 4 pour le droit \emph{lecture};
- \item 2 pour le droit \emph{écriture};
- \item 1 pour le droit \emph{exécution}.
- \end{itemize}
- Ces valeurs peuvent être additionnées. On analyse donc ainsi les
- permissions sur le fichier \verb|backup.sh|:
- \begin{itemize}
- \item utilisateur \verb|robert|, lecture + écriture: $4+2=6$;
- \item groupe \verb|robert|, lecture: $4$;
- \item reste du monde, lecture: $4$.
- \end{itemize}
- Soit $644$. Pour ajouter à l'utilisateur \verb|robert| seulement la
- permission en exécution, il faudrait donc porter cette valeur à
- $744$. Nous allons ici donner ce droit à la fois à \verb|robert|, au
- groupe \verb|robert| et au reste du monde, soit une valeur de
- $755$. La syntaxe est la suivante:
- \begin{minted}{text}
- chmod xyz <fichier>
- \end{minted}
- où \verb|xyz| sont les trois chiffres qui représentent les permissions.
- \begin{minted}[escapeinside=||, linenos]{text}
- [robert@kiddo courses]$ chmod 755 backup.sh
- [robert@kiddo courses]$ ls -l --color backup.sh
- -rwxr-xr-x 1 robert robert 82 17 sept. 22:06 |\textcolor{green}{backup.sh}|
- \end{minted}
- \begin{quoting}
- \textbf{Commentaire}:
- \begin{enumerate}
- \item La commande \verb|chmod| a été entrée à la ligne 1.
- \item À la ligne 2, nous avons lancé la commande
- \commande{ls}\mintinline{text}|ls -l --color| sur le fichier
- \verb|backup.sh|: les droits listés à la ligne 3 montrent bien que
- la valeur \verb|x| a été ajoutée aux trois endroits possibles. On
- voit enfin que l'option \verb|--color| affiche en vert les
- fichiers qui sont exécutables.
- \end{enumerate}
- \end{quoting}
- Nous pouvons désormais exécuter notre script:
- \begin{minted}[linenos,escapeinside=||]{text}
- [robert@kiddo courses]$ ls -l --color
- total 36
- -rwxr-xr-x 1 robert robert 82 17 sept. 22:06 |\textcolor{green}{backup.sh}|
- -rw-r--r-- 1 robert robert 165 16 sept. 19:40 bibliography.bib
- drwxr-xr-x 5 robert robert 4096 17 sept. 22:30 |\textcolor{blue}{fichiers}|
- -rw-r--r-- 1 robert robert 680 16 sept. 18:34 makefile
- -rw-r--r-- 1 robert robert 898 16 sept. 19:39 _preamble_bbl.tex
- -rw-r--r-- 1 robert robert 699 14 sept. 15:02 _preamble-ed.tex
- -rw-r--r-- 1 robert robert 719 16 sept. 19:39 _preamble.tex
- -rw-r--r-- 1 robert robert 1407 17 sept. 00:15 README.md
- -rw-r--r-- 1 robert robert 1804 17 sept. 00:15 README.tex
- [robert@kiddo courses]$ ./backup.sh
- adding: sauvegarde/ (stored 0%)
- adding: sauvegarde/README.tex (deflated 57%)
- adding: sauvegarde/_preamble.tex (deflated 45%)
- adding: sauvegarde/_preamble_bbl.tex (deflated 57%)
- adding: sauvegarde/_preamble-ed.tex (deflated 44%)
- [robert@kiddo courses]$ ls -l --color
- total 44
- -rwxr-xr-x 1 robert robert 82 17 sept. 22:06 |\textcolor{green}{backup.sh}|
- -rw-r--r-- 1 robert robert 165 16 sept. 19:40 bibliography.bib
- drwxr-xr-x 5 robert robert 4096 17 sept. 22:31 |\textcolor{blue}{fichiers}|
- -rw-r--r-- 1 robert robert 680 16 sept. 18:34 makefile
- -rw-r--r-- 1 robert robert 898 16 sept. 19:39 _preamble_bbl.tex
- -rw-r--r-- 1 robert robert 699 14 sept. 15:02 _preamble-ed.tex
- -rw-r--r-- 1 robert robert 719 16 sept. 19:39 _preamble.tex
- -rw-r--r-- 1 robert robert 1407 17 sept. 00:15 README.md
- -rw-r--r-- 1 robert robert 1804 17 sept. 00:15 README.tex
- drwxr-xr-x 2 robert robert 4096 17 sept. 22:31 |\textcolor{blue}{sauvegarde}|
- -rw-r--r-- 1 robert robert 2828 17 sept. 22:31 sauvegarde.zip
- [robert@kiddo courses]$ ls sauvegarde
- _preamble_bbl.tex _preamble-ed.tex _preamble.tex README.tex
- \end{minted}
- \begin{quoting}
- \textbf{Commentaire:}
- \begin{itemize}
- \item lignes 1--11: la commande \commande{ls}\verb|ls -l --color|
- donne l'état du dossier \emph{avant} l'exécution du script
- \verb|backup.sh|;
- \item lignes 12--17: exécution du script et messages du terminal;
- \item lignes 18--30: la commande \verb|ls -l --color| donne l'état du
- dossier \emph{après} l'exécution du script \verb|backup.sh|. On
- voit qu'un nouveau répertoire \verb|sauvegarde| a été créé, de
- même qu'un fichier archive \verb|sauvegarde.zip|;
- \item lignes 31--32: la commande \verb|ls sauvegarde| liste le
- contenu de ce répertoire. On y trouve tous les fichiers
- \verb|.tex| qui y ont été copiés par le script.
- \end{itemize}
- \end{quoting}
- \paragraph{PATH} Un dernier point reste à éclaircir: à la ligne 12,
- pourquoi a-t-on écrit \mintinline{bash}|./backup.sh| et non pas
- simplement \mintinline{bash}|backup.sh|? Tout simplement pour des
- raisons de sécurité. En effet, le principe est que les fichiers
- exécutables se trouvent dans certains répertoires-système spécialement
- conçus pour les accueillir. C'est pour cette raison que l'on peut
- lancer les commandes \verb|bash| sans avoir à les préfixer. Or notre
- répertoire de travail ne fait partie de ces répertoires spéciaux. Il
- faut donc préfixer tout script exécutable qui s'y trouve par son
- \emph{chemin d'accès}, soit relatif, soit absolu. On a choisi ici la
- première méthode: dans la séquence \mintinline{text}|./|, le point
- représente le répertoire courant tandis que le \emph{slash} précise
- qu'il s'agit d'un chemin d'accès. Sans le \emph{slash}, le
- \emph{shell} aurait compris le point comme un préfixe de fichier
- caché.
- \section{Les variables}
- \label{sec:les-variables}
- Les variables sont des informations temporaires qui peuvent être
- stockées et rappelées à tout moment. L'exemple qui suit va donner
- l'occasion d'étudier une nouvelle commande,
- \commande{echo}\verb|echo|, dont le rôle est justement de retourner
- au terminal la chaîne de caractères qu'on lui passe en argument:
- \begin{minted}[linenos]{text}
- [robert@kiddo courses]$ mysystem="Linux"
- [robert@kiddo courses]$ echo 'Mon système est $mysystem.'
- Mon système est $mysystem.
- [robert@kiddo courses]$ echo "Mon système est $mysystem."
- Mon système est Linux.
- \end{minted}
- \begin{quoting}
- \textbf{Commentaire:}
- \begin{description}
- \item[Définition] Pour \emph{définir une variable}, on lui donne un
- nom, suivi du signe $=$, suivi de sa définition entre guillemets
- droits.
- \item[Rappel] Pour \emph{rappeler} la valeur d'une variable, on
- saisit le nom qu'on lui a donné, préfixé par le signe \verb|$|.
- \end{description}
- \begin{mdframed}[backgroundcolor=Cyan]
- Observez la différence dans l'usage des guillemets!
- \href{./01-ligne-de-commande.pdf#lnk_guillemets}{Comme
- on le sait}, les guillemets servent à indiquer au \emph{shell}
- que les espaces ne sont pas des caractères actifs. Il y a
- cependant une grande différence entre les guillemets simples et
- les guillemets doubles: les premiers renvoient une expression
- littérale dans laquelle rien n'est interprété (lignes~2--3) tandis
- que les seconds permettent l'interprétation des variables
- (lignes~4--5).
- \end{mdframed}
- \end{quoting}
- Certaines variables sont automatiquement définies par \emph{bash}. Par
- exemple:
- \begin{enumerate}
- \item \verb|$0|: renvoie le nom du script en cours d'exécution.
- \item \verb|$1 - $9|: désignent les arguments successifs passés au
- script.
- \end{enumerate}
- Nous pouvons désormais perfectionner le script \verb|backup.sh|:
- \begin{minted}[linenos]{bash}
- #!/bin/bash
- echo "Veuillez choisir l'extension des fichiers à sauvegarder"
- echo "(sans le point):"
- read -p 'extension: ' ext
- echo "Veuillez choisir le nom du dossier de sauvegarde:"
- read -p 'dossier: ' backupdir
- mkdir "$backupdir"
- cp *.$ext "$backupdir"
- zip -r "$backupdir".zip "$backupdir"
- echo "Terminé. $0 a copié vos fichiers .$ext dans $backupdir"
- echo "et l'archive $backupdir.zip a été créée."
- \end{minted}
- \paragraph{read}
- \label{ref:read}
- \commande*{read} Cette nouvelle commande est expliquée dans
- le commentaire qui suit\footnote{Voir aussi plus loin
- \vpageref{ref:read-plus}.}:
- \begin{quoting}
- \textbf{Commentaire:}
- \begin{enumerate}
- \item Une nouvelle commande a été introduite ici: aux lignes~4 et 6,
- \verb|read| attend que l'utilisateur saisisse quelque chose au
- clavier. Ensuite, la valeur saisie au clavier est associée à une
- variable qui peut être reprise dans le script. On a également
- passé à \verb|read| l'option \verb|-p| qui permet d'ajouter un
- \emph{prompt} spécifique.
- \item Aux lignes 7, 8 et 9, on a pris la précaution de mettre entre
- guillemets doubles le rappel de la variable:
- \verb|"$backupdir"|. On sait en effet que beaucoup d'utilisateurs
- utilisent les espaces dans les noms des fichiers. Ici, la variable
- étant rappelée à l'intérieur de guillemets doubles, elle sera
- toujours interprétée correctement par \emph{bash}.
- \end{enumerate}
- \end{quoting}
- Exécution du script \verb|./backup.sh tex|:
- \begin{minted}[linenos]{text}
- [robert@kiddo courses]$ ./backup.sh
- Veuillez choisir l'extension des fichiers à sauvegarder
- (sans le point):
- extension: tex
- Veuillez choisir le nom du dossier de sauvegarde:
- dossier: Houba
- adding: Houba/ (stored 0%)
- adding: Houba/README.tex (deflated 57%)
- adding: Houba/_preamble.tex (deflated 45%)
- adding: Houba/_preamble_bbl.tex (deflated 57%)
- adding: Houba/_preamble-ed.tex (deflated 44%)
- Terminé. ./backup.sh a copié vos fichiers .tex dans Houba
- et l'archive Houba.zip a été créée.
- \end{minted}
- \begin{quoting}
- \textbf{Commentaire:}
- \begin{enumerate}
- \item Les valeurs attendues ont été saisies par l'utilisateur aux
- lignes~4 et 6.
- \item À la ligne~12, le script a utilisé la variable \verb|$0| pour
- retourner son propre nom.
- \end{enumerate}
- \end{quoting}
- \section{Captures et substitutions}
- \label{sec:capt-et-subst}
- Cette technique simple permet de transformer en variable le résultat
- d'une commande. Il suffit de placer la commande entre parenthèses
- précédées du signe $=$. Exemple:
- \begin{minted}[linenos]{text}
- [robert@kiddo courses]$ nbre=$(ls | wc -l)
- [robert@kiddo courses]$ echo $nbre
- 8
- \end{minted}
- \needspace{2\baselineskip}
- \begin{quoting}
- \textbf{Commentaire:}
- \begin{enumerate}
- \item À la ligne~1, la commande \commande{ls}\verb|ls| liste les
- fichiers, puis son résultat est interprété par
- \commande{wc}\verb|wc -l| qui compte le nombre de fichiers
- retournés.
- \item La variable \verb|$nbre| contient donc ce dernier résultat.
- \end{enumerate}
- \end{quoting}
- Utilisons cette technique pour perfectionner notre script
- \verb|backup.sh| (voir ci-dessous la ligne~9):
- \begin{minted}[linenos]{bash}
- #!/bin/bash
- echo "Veuillez choisir l'extension des fichiers à sauvegarder"
- echo "(sans le point):"
- read -p 'extension: ' ext
- echo "Veuillez choisir le nom du dossier de sauvegarde:"
- read -p 'dossier: ' backupdir
- mkdir "$backupdir"
- cp *.$ext "$backupdir"
- nbre=$(ls $backupdir/*.$ext | wc -l)
- zip -r "$backupdir".zip "$backupdir"
- echo "Terminé. $0 a copié $nbre fichiers .$ext dans $backupdir"
- echo "et l'archive $backupdir.zip a été créée."
- \end{minted}
- Exécution:
- \begin{minted}[linenos]{text}
- [robert@kiddo courses]$ ./backup.sh
- Veuillez choisir l'extension des fichiers à sauvegarder
- (sans le point):
- extension: tex
- Veuillez choisir le nom du dossier de sauvegarde:
- dossier: Houba
- adding: Houba/ (stored 0%)
- adding: Houba/README.tex (deflated 57%)
- adding: Houba/_preamble.tex (deflated 45%)
- adding: Houba/_preamble_bbl.tex (deflated 57%)
- adding: Houba/_preamble-ed.tex (deflated 44%)
- Terminé. ./backup.sh a copié 4 fichiers .tex dans Houba
- et l'archive Houba.zip a été créée.
- \end{minted}
- \section{Conditions}
- \label{sec:conditions}
- Il est souvent très utile de n'exécuter des lignes de code que si
- certaines conditions sont remplies. Reprenons ici l'exemple du script
- \verb|backup.sh|: au moment où le script crée le répertoire de
- sauvegarde, il ne contrôle pas si ce répertoire existe déjà, ce qui est
- un défaut. Voici comment on peut créer une condition adaptée à cette
- situation:
- \begin{minted}[linenos]{bash}
- if [ -d "$backupdir" ]
- then
- echo "Le dossier $backupdir existe déjà. Veuillez relancer le"
- echo "programme et saisir un autre nom."
- exit 1
- else
- mkdir "$backupdir"
- fi
- \end{minted}
- \begin{quoting}
- \textbf{Commentaire:}
- \begin{enumerate}
- \item Observez la structure de la condition:
- \begin{itemize}
- \item ligne 1: \commande*{if}\mintinline{bash}|if|
- $\rightarrow$ \emph{si <condition>} où la condition est posée
- entre crochets. Dans l'expression entre les crochets, %
- \verb|-d "$backupdir"|, \verb|-d| signifie: \verb|$backupdir|
- existe \emph{et} est un répertoire;
- \item ligne 2: \commande*{then}\mintinline{bash}|then|
- $\rightarrow$ \emph{alors, passez à la ligne suivante};
- \item ligne 6: \commande*{else}\mintinline{bash}|else|
- $\rightarrow$ \emph{autrement, passez à la ligne suivante};
- \item ligne 8: \commande*{fi}\mintinline{bash}|fi|
- $\rightarrow$ \emph{fin de la condition} (en fait les deux
- lettres de la conjonction \verb|if| écrite à l'envers).
- \end{itemize}
- \item À la ligne 5, la commande \commande{exit}\verb|exit 1|
- ordonne au programme de se terminer immédiatement et de renvoyer
- la valeur \verb|1| comme numéro d'état (\emph{status number}). Par
- convention, \verb|0| est pour \emph{true} (le programme a bien été
- exécuté) et \verb|1| est pour \emph{false} (il y a eu une erreur).
- \end{enumerate}
- \end{quoting}
- Voici donc comment se présente le script \verb|backup.sh| une fois la
- condition insérée:
- \captionof{listing}{bash: exemple de condition \texttt{if-then-else}%
- \label{lst:if-then-else}}
- \inputfile[linenos,highlightlines={7-14}]{bash}{backup.sh}
- \subsection{Conditions en série}
- \label{sec:conditions-en-serie}
- Supposons que le répertoire de sauvegarde ait été supprimé mais que
- l'archive \verb|.zip| correspondante ne l'ait pas été. Dans ce cas, le
- script \verb|backup.sh| l'écraserait. Pour éviter cela, nous pouvons
- utiliser dans le script l'instruction
- \commande*{elif}\verb|elif| qui permet de construire des
- conditions en série. Littéralement, \verb|elif| est pour \emph{else
- if}, \enquote{ou autrement, si\ldots}. Voici donc ce qu'il faut
- ajouter:
- \begin{minted}{bash}
- elif [ -e "$backupdir".zip ]
- then
- echo "L'archive $backupdir.zip existe déjà. Veuillez la supprimer"
- echo "ou la déplacer en dehors de ce dossier, puis relancez le"
- echo "programme."
- exit 1
- \end{minted}
- Ce qui donne le script \verb|backup-mk2.sh| suivant:
- \captionof{listing}{bash: instruction \texttt{elif}\label{lst:elif}}
- \inputfile[linenos,highlightlines={12-17}]{bash}{backup-mk2.sh}
- \subsection{Tests}
- \label{sec:tests}
- Le tableau suivant donne la liste des tests les plus répandus que l'on
- peut associer aux conditions. Les tests sont placés entre crochets
- droits comme le montre la ligne~7 du \vref{lst:if-then-else}.
- \begin{xltabular}{.77\linewidth}{lX}
- \toprule
- Opérateur & Description \\
- \midrule\endhead
- \verb|! expr| & \verb|expr| est faux \\
- \verb|-n str| & la longueur de \verb|str| $>0$\footnote{Comprendre:
- \texttt{str} existe.} \\
- \verb|-z str| & la longueur de \verb|str| $=0$\footnote{Comprendre:
- \texttt{str} n'existe pas.}\\
- \verb|str1 = str2| & \verb|str1| est égal à \verb|str2|\\
- \verb|str1 != str2| & \verb|str1| n'est pas égal à \verb|str2|\\
- \verb|int1 -eq int2| & les nombres \verb|int1| et \verb|int2| sont
- égaux\\
- \verb|int1 -gt int2| & \verb|int1| $>$ \verb|int2|\\
- \verb|int1 -lt int2| & \verb|int1| $<$ \verb|int2|\\
- \verb|-d fichier| & \verb|fichier| existe et est un répertoire\\
- \verb|-e fichier| & \verb|fichier| existe \\
- \verb|-s fichier| & \verb|fichier| existe et n'est pas vide (taille
- $>0$) \\
- \verb|-r fichier| & \verb|fichier| existe et est accessible en
- lecture\\
- \verb|-w fichier| & \verb|fichier| existe et est accessible en
- écriture \\
- \verb|-x fichier| & \verb|fichier| existe et est accessible en
- exécution \\
- \bottomrule
- \caption{tests\label{tab:tests}}
- \label{tab:tests}
- \end{xltabular}
- \paragraph{test}
- \commande*{test} À l'intérieur du script \verb|bash|, les
- crochets renvoient en fait à une commande par ailleurs disponible:
- \verb|test|. La commande \verb|test| renvoie en fait la sortie
- \verb|0| si le résultat est \emph{vrai} et \verb|1| si le résultat est
- \emph{faux}. Le terminal ne retourne pas le résultat, mais celui-ci
- est associé à une variable
- \verb|$?| que l'on peut afficher par la commande:
- \commande{echo}\mintinline{bash}|echo $?|. En voici quelques
- exemples:
- \begin{minted}[linenos]{text}
- [robert@kiddo courses]$ ls
- bibliography.bib makefile _preamble-ed.tex _preamble.tex README.tex
- fichiers _preamble_bbl.tex _preamble.log README.md
- [robert@kiddo courses]$ test -d fichiers
- [robert@kiddo courses]$ echo $?
- 0
- [robert@kiddo courses]$ test -x makefile
- [robert@kiddo courses]$ echo $?
- 1
- [robert@kiddo courses]$ test -e makefile
- [robert@kiddo courses]$ echo $?
- 0
- [robert@kiddo courses]$ test 0123 = 123
- [robert@kiddo courses]$ echo $?
- 1
- [robert@kiddo courses]$ test 0123 -eq 123
- [robert@kiddo courses]$ echo $?
- 0
- \end{minted}
- \begin{quoting}
- \textbf{Commentaire ---} Cet exemple montre quelle est la différence
- entre le test $=$ pour qui il faut une stricte équivalence dans les
- chaînes de caractères, et le test \verb|-eq| qui est un test
- arithmétique.
- \end{quoting}
- \subsection{Indentation}
- \label{sec:indentation}
- L'indentation est une technique qui consiste à donner aux lignes de
- code différentes profondeurs de marge à gauche de façon à distinguer
- clairement des blocs logiques. Les lignes 7--20 du
- \vref{lst:if-then-else} en donnent un exemple. L'indentation permet de
- faire apparaître clairement ce qui dépend de l'instruction
- \commande{if}\verb|if| (l.~7), puis \commande{elif}\verb|elif|
- (l.~12) et enfin \commande{else}\verb|else| (l.~18).
- Cette technique est commune à tous les langages informatiques et tous
- les programmeurs l'utilisent. En Python, que nous étudierons plus
- tard, l'indendation fait même partie du langage lui-même, puisque
- Python a recours à l'indentation pour structurer les blocs logiques, à
- la différence d'autres langages qui ont recours soit à des
- déclarations explicites telles que \verb|begin ... end| ou encore à
- des opérateurs tels que \verb|{ ... }|
- Voici un exemple d'indentation d'une structure dans laquelle on a
- placé une condition à l'intérieur d'une autre condition:
- \captionof{listing}{bash: exemple d'indentation}
- \inputfile[linenos]{bash}{greaterthan.sh}
- \subsection{Opérateurs booléens}
- \label{sec:operateurs-booleens}
- Les opérateurs booléens, dont le nom est tiré du mathématicien anglais
- George Boole, sont des opérateurs logiques que l'on peut associer à
- des conditions. En voici deux ici:
- \begin{enumerate}
- \item \verb+||+ pour \emph{ou};
- \item \verb|&&| pour \emph{et}.
- \end{enumerate}
- Par exemple, dans le \vref{lst:elif} ci-dessus, on pourrait remplacer
- les lignes 7--20 par le code ci-dessous:
- \begin{minted}[linenos]{bash}
- if [ -d "$backupdir" ] || [ -e "$backupdir".zip ]
- then
- echo "Le dossier $backupdir et/ou l'archive $backupdir.zip"
- echo "existent déjà. Veuillez relancer le programme et saisir"
- echo "un autre nom."
- exit 1
- else
- mkdir "$backupdir"
- fi
- \end{minted}
- \subsection{case}
- \label{sec:case}\commande*{case}
- \verb|case| est une instruction qui permet d'exécuter différentes
- actions en fonction de la valeur d'une variable. Elle est intéressante
- à étudier ici car elle fait appel à la fois à la notion de variable et
- aux expressions régulières. La syntaxe est la suivante:
- \begin{minted}[linenos]{bash}
- case $var in
- expr1)
- <commandes ...>
- ;;
- expr2)
- <commandes ...>
- ;;
- esac
- \end{minted}
- \begin{quoting}
- \textbf{Commentaire:}
- \begin{enumerate}
- \item L'instruction \verb|case| se termine par \verb|esac|, de même,
- comme on l'a vu, que l'expression \verb|if| se termine par
- \verb|fi|.
- \item Les expressions régulières sont suivies d'une parenthèse
- fermante.
- \item Pour passer d'un jeu de commande au jeu suivant, on écrit, sur
- une ligne \verb|;;|
- \end{enumerate}
- \end{quoting}
- Le \vref{lst:case} montre un exemple facile à comprendre de cette
- technique.
- \captionof{listing}{bash: instruction \texttt{case}\label{lst:case}}
- \inputfile[linenos]{bash}{animal.sh}
- \begin{quoting}
- \textbf{Commentaire:}
- \begin{enumerate}
- \item À la ligne 4, l'utilisateur est invité à entrer une
- réponse. L'instruction \commande{read}\verb|read| associe la
- réponse à la variable \verb|animal|.
- \item À la ligne 6, l'instruction \verb|case| reprend la variable
- qui est donc préfixée par le signe \verb|$|.
- \item Aux lignes 7 et 10, on permet à l'utilisateur d'entrer soit le
- mot \emph{chien}, soit le mot \emph{chat}. L'initiale peut être
- une minuscule ou une majuscule.
- \item À la ligne 13, comme le signe \verb|*| est en \emph{bash} le
- \emph{wildcard} qui représente toute séquence de caractères, la
- commande de la ligne~14 sera forcément exécutée si les tests des
- lignes~7 et~10 ont échoué.
- \end{enumerate}
- \end{quoting}
- \begin{quoting}
- \textbf{Remarque:} à la place de l'instruction \verb|case|, on
- recommande aujourd'hui d'utiliser une nouvelle instruction,
- \commande{switch}\verb|switch|, dont la syntaxe, plus complexe,
- n'est pas étudiée ici.
- \end{quoting}
- \section{Boucles}
- \label{sec:boucles}
- Les boucles (en anglais: \emph{loops}) servent à indiquer qu'une série
- d'instructions doit reprendre et continuer à s'exécuter aussi
- longtemps qu'une condition donnée est remplie ou n'est pas remplie.
- Prenons un exemple simple. Nous avons dans un répertoire plusieurs
- centaines d'images de différents formats et nous souhaitons convertir
- au format \verb|.png| toutes les images qui sont enregistrées au
- format \verb|.tiff|.
- Pour convertir une seule image, nous pouvons utiliser l'outil en ligne
- de commande \verb|convert| fourni par le programme
- \emph{imagemagick}\footnote{\url{http://www.imagemagick.org}}. Pour
- convertir une seule image, la syntaxe est la suivante:
- \begin{minted}{bash}
- convert image.tiff image.png
- \end{minted}
- Mais comment faire pour en convertir un grand nombre pris dans un
- répertoire qui en compte des centaines enregistrées dans des formats
- différents?
- \paragraph{basename}
- \commande*{basename}Avant de continuer, il faut dire un mot de la
- commande \verb|basename| que nous allons utiliser ici. Cette commande
- permet de dépouiller un nom de fichier de son chemin d'accès et de son
- extension. La syntaxe est la suivante:
- \begin{minted}[escapeinside=||]{text}
- basename -s .|\emph{ext}| |\emph{file}|
- \end{minted}
- où \emph{ext} est le nom de l'extension et \emph{file} le nom du
- fichier. Voici un exemple:
- \begin{minted}{text}
- [robert@kiddo fichiers]$ ls -l images/
- total 252
- -rw-r--r-- 1 robert robert 96404 2 juil. 18:32 02-ascii.png
- -rw-r--r-- 1 robert robert 33951 20 juin 19:59 02-donnees1.png
- -rw-r--r-- 1 robert robert 11503 20 juin 20:01 02-donnees2.png
- -rw-r--r-- 1 robert robert 17450 3 juil. 17:43 02-exercice_formats.png
- -rw-r--r-- 1 robert robert 87510 14 sept. 15:06 02-unicode.png
- [robert@kiddo fichiers]$ basename -s .png images/*
- 02-ascii
- 02-donnees1
- 02-donnees2
- 02-exercice_formats
- 02-unicode
- \end{minted}
- \paragraph{for-do-done}
- \commande*{for}\commande*{do}\commande*{done}
- La boucle que nous allons utiliser fait appel à trois instructions:
- \begin{enumerate}
- \item \verb|for| prend comme argument un nom qui sera ensuite traité
- comme une variable. Le nom de la variable est suivi de l'instruction
- \commande{in}\verb|in| et d'un nom de fichier qui contient des
- données séparées par des espaces ou bien d'une expression dans
- laquelle on a placé des
- \href{./01-ligne-de-commande.pdf#lnk_wildcards}{\emph{wildcards}}.
- \item \verb|do| est suivi des commandes qu'il faut exécuter sur chaque
- élément retourné par l'instruction \verb|for| \ldots{} \verb|in|.
- \item \verb|done| marque la fin de la boucle.
- \end{enumerate}
- Voici maintenant le script qui assure la conversion du format
- \verb|.tiff| vers le format \verb|.png|:
- \captionof{listing}{bash: \texttt{for ... do ... done}}
- \inputfile[linenos]{bash}{tiff2png.sh}
- \begin{quoting}
- \textbf{Commentaire:}
- \begin{enumerate}
- \item Comprendre ainsi le début de la ligne~2 %
- (\mintinline{bash}|for file in|): \enquote{pour tout \texttt{file}
- dans\ldots} où \verb|file| définit une variable dont la valeur
- pourra ensuite être rappelée par \verb|$file|. Le reste de la
- ligne est une \emph{double capture} (v.~\emph{supra},
- \vref{sec:capt-et-subst}):
- \begin{enumerate}
- \item \verb|$(ls *.tiff)| liste et retourne tous les fichiers dont
- l'extension est \verb|.tiff|
- \item \verb|$(basename -s .tiff $(ls *.tiff))| demande à
- \verb|basename| de dépouiller tous ces fichiers de leur
- extension \verb|.tiff| et retourne la liste des noms seuls.
- \end{enumerate}
- \item La variable \verb|file| est donc une liste constituée des noms
- de tous les fichiers \verb|.tiff| sans leur extension. La ligne~3
- les convertit tous les uns après les autres vers le format
- \verb|.png|.
- \end{enumerate}
- \end{quoting}
- \paragraph{while}
- \commande*{while} L'expression \verb|while| exécute les
- lignes de code qui suivent aussi longtemps qu'un test donné retourne
- un résultat positif (\enquote{vrai}, en anglais \emph{true}). Le
- script \verb|countlines.sh|, donné dans le \cref{lst:countlines}
- ci-dessous, utilise cette expression pour compter les lignes des
- fichiers\footnote{Pour une autre façon plus simple d'écrire le même
- programme, voir le \vref{lst:countlines-mk2}.}.
- \captionof{listing}{bash: \texttt{while}\label{lst:countlines}}
- \inputfile[linenos,highlightlines={13,16-17}]{bash}{countlines.sh}
- \paragraph{read}
- \label{ref:read-plus}\commande*{read}
- On a déjà présenté plus haut \vpageref{ref:read} cette
- instruction. Pour bien comprendre le \cref{lst:countlines}, il faut
- savoir que la fonction de \verb|read| est de \emph{lire une ligne},
- quelle que soit cette ligne, depuis l'entrée standard du \emph{shell}
- (en anglais: \emph{standard input}). Dans le \vref{lst:case}, la ligne
- en question était donc constituée d'une saisie au clavier dans
- laquelle la ligne était formée par une chaîne de caractères terminée
- par un \emph{retour charriot} (\emph{carriage return}).
- \begin{mdframed}[backgroundcolor=Cyan]
- \verb|read| admet aussi une option \verb|-r| qui est importante:
- quand cette option est activée, le caractère \verb|\|
- (\emph{backslash}) est traité comme un caractère ordinaire, et non
- comme un caractère actif. Pour simplement compter les lignes d'un
- fichier, il faut ajouter cette option car en \emph{bash} le
- \emph{backslash} est précisément un caractère actif: à la fin d'une
- ligne, il sert à joindre cette ligne à la ligne suivante.
- \end{mdframed}
- Étudions de près les lignes~13--17 du \cref{lst:countlines}:
- l'instruction \verb|while| (l.~13) se termine à la ligne~17 par
- \verb|done|. Et aussitôt à ce moment,
- \href{./01-ligne-de-commande.pdf#lnk_redirection}{l'opérateur
- de redirection} \mintinline{bash}|<| lit le contenu du fichier dont
- le nom correspond à la variable \verb|file| et le passe en argument à
- la boucle \verb|while ... done|.
- \paragraph{Commentaire du \cref{lst:countlines}}
- \begin{enumerate}
- \item À la ligne~6, on crée une variable \verb|numline| à laquelle
- on attribue la valeur \verb|0|. Elle servira donc de compteur.
- \item L'instruction essentielle est à la ligne~13:
- \mintinline{bash}|while read -r line|: comme ce qui suit
- \verb|while| \emph{est un test}, ce test donnera un résultat positif
- aussi longtemps que la variable \verb|line| existe, c'est-à-dire
- aussi longtemps que l'opérateur de redirection \mintinline{bash}|<|
- de la ligne~17 envoie des lignes terminées par un retour
- charriot. Tant que cette condition est remplie, la commande de la
- ligne~16 sera exécutée, et le compteur de lignes sera incrémenté
- d'une unité à chaque fois.
- \item Les instructions des lignes~20--25 sont faciles à
- suivre. Étudiez-les à l'aide du \vref{tab:tests}: vous pourrez
- comprendre comment la programmation peut être adaptée pour suivre
- les règles de l'orthographe française.
- \end{enumerate}
- Exécutons maintenant le script:
- \begin{minted}{text}
- [robert@kiddo courses]$ ./countlines.sh
- Entrez le nom du fichier dont vous voulez compter les lignes:
- Fichier: whack
- Erreur: le fichier whack n'existe pas.
- [robert@kiddo courses]$ ./countlines.sh
- Entrez le nom du fichier dont vous voulez compter les lignes:
- Fichier: makefile
- Votre fichier makefile compte 21 lignes.
- \end{minted}
- \paragraph{until}
- \commande*{until}À la différence de \verb|while|,
- \verb|until| exécute les instructions qui suivent jusqu'au moment où
- le résultat du test associé à \verb|until| devient \emph{positif}
- (\emph{true}). Pour prendre un exemple très simple, le script suivant,
- que l'on appellera \verb|rah.sh|, demande combien de fois on souhaite
- tirer la queue d'un lion:
- \captionof{listing}{bash: comment faire rugir le lion?}
- \inputfile[linenos]{bash}{rah.sh}
- \begin{quoting}
- \textbf{Commentaire:}
- \begin{enumerate}
- \item La condition posée à la ligne~17 se comprend ainsi:
- \enquote{faites ce qui suit jusqu'à ce que le compteur de
- rugissements atteigne une valeur supérieure à celle définie par
- l'utilisateur. Si la valeur est supérieure, sortez de la boucle.}
- \item À la ligne~20, on demande à \emph{bash} d'attendre une seconde.
- \item À la ligne~21, on incrémente de 1 la valeur du compteur de
- rugissements (voir ci-dessus le \vref{lst:countlines}, l.~14), puis
- on reprend la boucle à la ligne~17.
- \end{enumerate}
- \end{quoting}
- Exécution du script \verb|rah.sh|:
- \begin{minted}{text}
- [robert@kiddo courses]$ ./rah.sh
- Combien de fois tirez-vous la queue du lion? 3
- Raaaaaaaaahhhhhhhhhhhh! (1)
- Raaaaaaaaahhhhhhhhhhhh! (2)
- Raaaaaaaaahhhhhhhhhhhh! (3)
- [robert@kiddo courses]$
- \end{minted}
- \paragraph{break}
- \commande*{break} \verb|break| est une instruction qui
- ordonne de quitter immédiatement la boucle dans laquelle on se
- trouve. Supposons par exemple que l'on écrive un programme dans lequel
- on souhaite limiter une action telle que la copie de
- fichiers. L'instruction \verb|break| sera exécutée dès que la limite
- est atteinte. Appelons ce script \verb|copyten.sh|:
- \captionof{listing}{bash: copie d'un nombre limité de
- fichiers\label{lst:copyten}}
- \inputfile[linenos]{bash}{copyten.sh}
- Ce script donne un exemple de code commenté. Comme on le voit, les
- commentaires peuvent se trouver sur des lignes isolées aussi bien que
- sur des lignes qui contiennent des commandes\footnote{Voir ci-dessus,
- \vref{sec:les-commentaires}.}.
- Exécution du script \verb|copyten.sh|:
- \begin{minted}{text}
- [robert@kiddo courses]$ ls *.txt
- 01.txt 03.txt 05.txt 07.txt 09.txt 11.txt
- 02.txt 04.txt 06.txt 08.txt 10.txt 12.txt
- [robert@kiddo courses]$ ./copyten.sh
- Attention: ce programme copie au maximum 10 fichiers.
- Que souhaitez-vous copier: *.txt
- Répertoire de destination: Houba
- Erreur: la destination doit être un répertoire.
- Le cas échéant, utilisez "mkdir Houba"
- pour créer le répertoire de destination.
- [robert@kiddo courses]$ mkdir Houba
- [robert@kiddo courses]$ ./copyten.sh
- Attention: ce programme copie au maximum 10 fichiers.
- Que souhaitez-vous copier: *.txt
- Répertoire de destination: Houba
- Terminé. 10 fichiers au maximum ont été copiés dans Houba.
- [robert@kiddo courses]$ ls Houba/
- 01.txt 02.txt 03.txt 04.txt 05.txt 06.txt 07.txt
- 08.txt 09.txt 10.txt
- \end{minted}
- \paragraph{continue}
- \commande*{continue} À l'inverse de \verb|break|,
- \verb|continue| demande à \emph{bash} d'interrompre l'itération
- courante mais sans quitter la boucle, puis de reprendre la boucle à
- partir de l'itération suivante. C'est une façon de prévoir des
- exceptions. Par exemple, nous pouvons modifier le script
- \ref{lst:copyten} comme suit:
- \captionof{listing}{bash: copie d'un nombre limité de
- fichiers sans échec si le fichier source n'existe pas}
- \inputfile[linenos,highlightlines={26-31}]{bash}{copyten-mk2.sh}
- \begin{quoting}
- \textbf{Commentaire:}
- \begin{enumerate}
- \item À la ligne~30, l'instruction \verb|continue| intervient dans
- le cas où le fichier à copier n'existe pas (voir le test de la
- ligne~26).
- \item Dans ce cas, le fichier est créé par la ligne~28.
- \item Puis \verb|continue| interrompt la boucle et la reprend depuis
- la ligne~24.
- \end{enumerate}
- \end{quoting}
- Voici ce que donne l'exécution de ce script:
- \begin{minted}{text}
- [robert@kiddo courses]$ ./copyten-mk2.sh
- Attention: ce programme copie au maximum 10 fichiers.
- Que souhaitez-vous copier: tchic.txt
- Répertoire de destination: Houba
- création de tchic.txt qui n'existe pas...
- Terminé. 10 fichiers au maximum ont été copiés dans Houba.
- [robert@kiddo courses]$ ls Houba/
- tchic.txt
- \end{minted}
- \section{Les fonctions}
- \label{sec:les-fonctions}
- Comme leur nom l'indique, les fonctions permettent d'exécuter des
- instructions de façon autonome dans un script. Elles peuvent être
- ensuite utilisées aussi souvent que nécessaire et permettent donc de
- réduire la taille du script. Deux formats sont permis:
- \begin{minted}[linenos]{bash}
- # Format 1
- ma_fonction () {
- <commandes>
- }
- # Format 2
- function ma_fonction {
- <commands>
- }
- \end{minted}
- Ces deux formats sont strictement équivalents. Le premier est le plus
- répandu car il rappelle ce qui se fait dans de nombreux autres
- langages où l'on utilise les parenthèses pour passer aux fonctions des
- variables, des chaînes de caractères, des paramètres optionnels ou
- même d'autres fonctions. Toutefois, en \emph{bash}, on ne met jamais
- rien entre les parenthèses qui sont purement décoratives.
- Le script suivant permet de parvenir au même résultat que le script
- qui a été présenté dans le \vref{lst:countlines}:
- \captionof{listing}{bash: exemple de
- fonction\label{lst:countlines-mk2}}
- \inputfile[linenos]{bash}{countlines-mk2.sh}
- Pour terminer, exécutons cette nouvelle version de notre script
- ici appelée \verb|countlines-mk2.sh|:
- \begin{minted}{text}
- [robert@kiddo courses]$ ./countlines-mk2.sh
- Entrez le nom du fichier dont vous voulez compter les lignes:
- Fichier: makefile
- Votre fichier makefile compte 21 lignes.
- \end{minted}
- \printindex[cmds]
- \end{document}
|