Partie 1 - Découverte de Bash.
Auteur : Franck CHAMBON, enseignant au lycée Lucie AUBRAC de Bollène (84).
Le document suivant est placé sous licence libre CC - BY-NC-SA 4.0
Une interface en ligne de commande (en anglais command line interface, couramment abrégé CLI) est une interface homme-machine (IHM) dans laquelle la communication entre l'utilisateur et l'ordinateur s'effectue en mode texte.
On appelle shell ou émulateur de terminal un logiciel qui permet de lancer des commandes en mode texte.
Bash est un langage de script libre, un interpréteur de commandes systèmes que l'on retrouve sous Linux, MacOS (enfin, presque...), Windows (depuis la version 10), et Android (via Termux par exemple). Il tire ses origines d'un logiciel écrit par Stephen Bourne en 1977 ; il garde certains aspects difficiles, mais reste incontournable.
Cette première série d'exercices n'utilise presque pas l'éditeur, on présente et on s'exerce sur les possibilités basiques de Bash.
Cette approche donne tous les outils nécessaires pour la partie 3 où on va créer des fichiers de tests qui serviront à comparer et confronter des algorithmes différents, ou écrits avec des langages de programmation différents.
Cette partie pourra paraître dense, technique, avec beaucoup de contenu. Ce n'est pourtant qu'une première approche de Bash qui est un langage délicat à apprendre.
Il est conseillé de bien taper chaque commande proposée, de bien lire les sections « À retenir ».
Vous pourrez revenir sur cette partie 1 après avoir travaillé les autres parties qui seront plus simples.
Après avoir étudié la partie 0 (Installations avec Termux), vous devriez pouvoir répondre aux questions suivantes :
$ et >>> sont les invites de commandes par défaut de Bash, et Python.
Bash et Python sont des langages de script, et Bash est plus ancien.
Dans $ apt install man, apt est une commande et, install et man sont des arguments.
Ctrl+D indique une fin de saisie, et Ctrl+C indique une interruption.
Dans $ man apt, apt est un argument et, man une commande dont on sort avec Q.
textes avec la commande $ mkdir 'textes'.$ cd textes.$ cd t puis d'appuyer sur la touche Tab ⭾, et Bash complète automatiquement textes, on parle d'autocomplétion. Très utile !$ nano prénom.txt, créer un fichier qui contient sur une ligne votre prénom (ou pseudo).métiers.txt qui contient sur une ou plusieurs lignes des métiers rêvés.$ cat prénom.txt métiers.txtmkdir : Créer un répertoire (make directory)cd : Changer de répertoire (change directory)cat : Concaténer un ou plusieurs fichiers, pour les afficher.Nous verrons progressivement comment faire en ligne les actions possibles à la souris avec un gestionnaire de fichiers ; et que les méthodes en ligne sont bien plus puissantes.
lolcat à la place de cat.Au lieu de taper
$ lolcat prénom.txt métiers.txt, il suffit d'appuyer sur ↑ afin de remonter dans l'historique de la ligne de commande, retrouver la dernière commande de l'exercice précédent, appuyer sur la touche Home du clavier, et ajouterlol.
$ lolcat -a prénom.txt métiers.txt. On dit que -a est une option, on peut découvrir les options d'une commande avec man, ici par exemple $ man lolcat.lolcat n'est qu'une commande jouet.man. Il est possible d'avoir les pages du manuel en français en fonction de l'installation système. Il est recommandé d'avoir un minimum de vocabulaire pour lire en anglais les pages de manuel.echo est une commande qui affiche une ligne de texte, et avec un saut le ligne à la fin.
-n est une option dont l'effet est justement de ne pas ajouter le saut de ligne à la fin.-e est une option qui permet d'échapper le caractère \ et de donner un sens à :
\n : un saut de ligne\t : une tabulation horizontalePour découvrir les autres séquences d'échappement, entrer
$ man echo
$ echo Salut Salut $ echo -n 'Bizarre ?' Bizarre ?$ echo 'Non !' Non ! $ echo -e 'ligne 1\nligne2\n\tmot décalé' ligne 1 ligne2 mot décalé $
-n) ; Pas de saut de ligne, donc le prompt se retrouve collé à la suite.-e) ; il y a des caractères échappés qui autorisent ici des sauts de ligne et des tabulations. Il en existe d'autres ; voir avec $ man echo.$ echo ...à vous de remplacer cette partie... \n saut de ligne \t tabulation $
Une solution est :
$ echo -e '\\ \bn\tsaut de ligne\n\\ \bt\ttabulation'
Expliquer cette solution après avoir bien lu $ man echo.
\n représente un saut de ligne.\t représente une tabulation horizontale.cowsay dans la partie 0 - Installations. Lire le manuel avec $ man cowsay. En utilisant l'option -e 'XX' et aussi l'option -T à compléter, obtenir l'affichage du dessin :__________________________
< Je tire aussi la langue. >
--------------------------
\ ^__^
\ (XX)\_______
(__)\ )\/\
U ||----w |
|| ||
Expliquer avec une phrase le rôle, ici, des options choisies.
On a vu comment :
mkdircdnano ou microcatVoyons comment faire la liste des fichiers dans un répertoire avec la commande ls, et ses options...
$ touch machin$ mkdir bidule$ ls$ ls -a$ ls -l$ ls *m*$ rmdir bidule$ rm machintouch crée un fichier vide s'il n'existe pas ; sinon modifie juste sa date de dernier accès. Ici, le fichier machin est créé, sans extension ; ce n'est pas un problème.bidule est créé, il est vide.-a pour all (tous), affiche aussi les fichiers et répertoires cachés. On note la présence ici de :
. : le répertoire actuel.. : le répertoire parent.-l donne des détails longs sur les propriétaires des fichiers et des répertoires, le groupe auxquels ils appartiennent, ainsi que les droits en écriture, lecture et exécution associés, mais aussi sur les dates de création. Ces nombreux détails sont utiles à l'administration du système. Nous en reparlerons dans la partie 4.*m* n'est pas une option, c'est un motif qui sert à filtrer les résultats qu'on souhaite afficher. Ici, n'importe quoi * (même rien), suivi d'un m, suivi de n'importe quoi *. Concrètement tout ce qui contient un m donc. Dans la partie 4, nous découvrirons davantage les motifs.bidule.machin.$ man rmdir, existe-t-il une commande pour effacer un répertoire non vide ?$ ls m*t* ?? remplace n'importe quel caractère. En général, que fait la commande $ ls image???.jpgpwd ? Penser à man.-h ou --help, comme avec $ touch --helptouch.ls ; il y a des options pour l'administration système, où les droits sur les fichiers ont un rôle important.rm.rmdir.* remplace toute chaîne de caractères, même vide ;? remplace un seul caractère.pwd.Entrer toutes les commandes suivantes dans l'ordre et en pensant à la touche Tab ⭾:
$ cd$ mkdir tex$ touch tex/pre.sty$ mkdir tex/asymtote$ touch tex/asymtpte/tri.asy$ tree tex*$ cd texte$ ls$ cd ~/tex/asymptote$ ls$ cd -$ ls$ cd && rm -rf texExplications :
$ cd, sans argument, déplace le répertoire de travail au répertoire de l'utilisateur, son home. Il existe un répertoire parent, mais l'utilisateur n'en est pas propriétaire. À partir de son home, l'utilisateur peut être propriétaire de tous les fichiers et répertoires./ qui sépare ; il indique le chemin à suivre.tri.asy avec un chemin un peu plus long.$ tree tex* affiche l'arborescence des répertoires et fichiers, du motif qui commence par tex ; pour nous, tex et textes répondent à ce motif.~) obtenu avec AltGr+2é sur un clavier Azerty.&&) on enchaîne avec une autre commande, on supprime le répertoire tex, récursivement (-r), et en force (-f).tree affiche de manière graphique l'arborescence ; utile pour visualiser la structure d'arbre des répertoires de votre projet. La racine est en haut !$ cd peut s'utiliser sans argument (retour au home de l'utilisateur), ou $ cd - qui revient au précédent répertoire de travail. Utile lorsqu'on veut naviguer rapidement entre deux répertoires non parents./, finissant par un fichier ou non, et commençant par ~ ou non, ou bien par /. Ils indiquent un répertoire ou un fichier distant. S'il commence par / ou par ~, c'est un chemin absolu, sinon il est relatif au répertoire actuel de travail.&& ; seulement si la première réussit sans échec, alors l'autre sera lancée. C'est une forme de structure conditionnelle.~/Images/2020*../*.txt../../projet/doc/src/inf?.???./secret/../secret.txt/cléNous avons pu créer des fichiers vides. Sans utiliser d'éditeur, montrons comment créer un petit fichier non vide, et comment faire un petit ajout à un fichier déjà existant.
> et >>Tester dans l'ordre les commandes suivantes :
$ echo 3 > test$ cat test$ echo 1000 >> test$ cat test$ echo -e "2222\n999999" >> test$ cat test$ echo oups > test$ cat test$ rm test$ cat test$ cat test.La redirection du résultat affiché vers un fichier n'est pas propre à la commande
echo, mais est valable pour toute commande qui produit un affichage.
Pour créer un fichier qui récupère l'affichage d'une commande :
$ commande [arguments] > fichierPour ajouter à un fichier l'affichage d'une commande :
$ commande [arguments] >> fichierDans ces deux cas, l'affichage n'est pas effectué, il a été redirigé.
textes de votre répertoire personnel.prénom.fig contenant votre prénom (ou votre pseudo) en FIGlet.prénom.fig le résultat qui pourrait être :_____ _ | ___| __ __ _ _ __ ___| | __ | |_ | '__/ _` | '_ \ / __| |/ / | _|| | | (_| | | | | (__| < |_| |_| \__,_|_| |_|\___|_|\_\
> permet de rediriger la sortie d'une commande vers un fichier qui est effacé avant l'exécution de la commande.>> permet de rediriger la sortie d'une commande vers un fichier auquel on ajoute la sortie.$ catOn observe que $ cat se contente d'afficher directement ce qui est tapé. Au lieu de l'afficher, on peut rediriger la sortie vers un fichier.
Suivre les instructions :
$ cat > surprise$ cat surprise. Penser à l'historique de commande !$ cat > fichier, soit en redirigeant également la sortie d'une autre commande vers le fichier, avec l'opérateur >.>>.De la même manière qu'on peut rediriger l'affichage vers un fichier, on peut rediriger l'entrée clavier par un fichier. Ce qui est contenu dans un fichier fera office d'argument à une commande. Voyons donc comment utiliser l'opérateur <.
<Dans le répertoire ~/textes, vous avez deux fichiers prénom.txt et métiers.txt construits au début de cette activité ; travaillons avec.
$ cd ~/textes$ figlet < prénom.txt$ figlet < métiers.txt > métiers.fig$ lolcat -a métiers.fig< permet de rediriger l'entrée d'une commande à partir d'un fichier au lieu de lire le clavier.|On va brancher la sortie d'une commande directement sur l'entrée d'une autre commande, le tout sans passer par un affichage intermédiaire.
L'idée est de faire :
$ commande1 > temporaire$ commande2 < temporaireMais sans passer explicitement par le fichier nommé temporaire. Une raison étant : le fichier temporaire existe-t-il déjà ? Il ne faudrait pas effacer un fichier déjà présent utile à une autre commande ! Une solution serait de fabriquer un nom aléatoire (ou dépendant de l'heure précise), mais rien ne garantit qu'il n'existe pas déjà...
La solution est d'utiliser un pipeline | :
commande1 | commande2La sortie de commande1 est automatiquement redirigée vers l'entrée de commande2, et Bash gère l'utilisation d'un fichier temporaire unique de manière transparente.
$ figlet < métiers.txt | lolcat -aOn peut enchaîner des pipeline comme dans l'exemple :
$ fortune | cowsay | lolcat| permet de brancher la sortie d'une commande sur l'entrée d'une autre commande. Dans une situation de travail à la chaîne dans une usine, on procède de la façon $ com1 | com2 | ... | comk, le travail est découpé en fonctions élémentaires. Chaque commande étant censée faire une chose simple, mais la faire bien.Nous avons évoqué le problème de créer un fichier temporaire avec un nom qui n'existe pas déjà, ou alors le souhait d'enregistrer un travail avec un nom de fichier qui reprend la date et l'heure d'enregistrement. Voyons comment faire.
La commande $ date, sans argument, affiche la date et l'heure avec un format prédéfini, dans une langue qui dépend d'un paramètre local.
Il est possible d'obtenir l'heure très précise, soit de l'instant actuel, soit d'un autre instant que l'on demanderait. Exemples :
Nous ne verrons pas de commandes aussi complexes, juste nous indiquons que c'est techniquement possible.
Étudions un exemple issu de $ man date :
$ date --date='@2147483647' Tue Jan 19 04:14:07 CET 2038
Dans cet exemple, on demande la date plus de 2 milliards de secondes après l'epoch d'Unix (l'origine du temps en Unix, le 1er janvier 1970 00:00:00 UTC.)
Le nombre 2147483647 est le plus grand entier représenté dans un conteneur d'entier signé sur 32 bit, on obtient la date du bug de l'an 2038.En pratique, on se sert souvent de
datedans un script pour récupérer l'heure ou la date présente, c'est utile pour nommer un fichier temporaire, par exemple.
$ man date, créer en une seule commande un fichier année.txt contenant l'année en cours. Cette commande doit rester valable pour les années à venir...Aide : Le manuel indique
date +[FORMAT]; il suffit de remplacer[FORMAT]par une séquence donnée qui correspond à l'année en cours.
_ _____ ____ ___ / |___ ||___ \ / _ \ | | / (_) __) | (_) | | | / / _ / __/ \__, | |_|/_/ (_)_____| /_/
Une commande (Bash, ou autre) peut prendre un certain temps d'exécution.
$ timecommande [arguments] exécute la commande avec ses arguments éventuels et affiche ensuite le temps écoulé.
On peut l'utiliser pour chronométrer l'exécution d'un script Python, ou d'un autre programme compilé.
$ time python mon_script.py < mon_entrée > ma_sortie
La commande python lit un script qui prend mon_entrée comme entrée standard (sans avoir besoin de lire au clavier), puis écrit le résultat dans un fichier ma_sortie, et enfin affiche à l'écran le temps écoulé.
Les compétitions d'algorithmique imposent, par exemple, qu'un programme doit résoudre un problème particulier en une durée annoncée.
Pour imposer une limite de temps de 1 seconde au programme présenté plus haut, on peut écrire :
$ timeout 1 time python mon_script.py < mon_entrée > ma_sortie
Dans le cadre des entraînements, que l'on retrouve sur FranceIOI par exemple, il est intéressant de vérifier que ma_sortie correspond bien à la sortie_officielle. Il nous faut donc des outils pour comparer des fichiers.
On rappelle que dans la partie 3, on va justement créer des jeux de tests d'entrée sortie pour nos programmes. Ainsi, on commence à avoir presque tous les outils pour ce faire !
La commande cp permet de copier un ou plusieurs fichiers source décrit par un nom ou un motif.
$ cp source destinationsource peut être un fichier, et alors destination est un répertoire ou un fichier.source peut être plusieurs fichiers (via un motif par exemple), et alors destination est un répertoire.$ mkdir ~/textes/cptests
$ cd ~/textes$ cp *.txt cptests$ cp prénom.txt cptests/prénom.copie.txt$ cmp cptests/prénom.txt cptests/prénom.copie.txtecho 'première copie' > cptests/prénom.txtecho 'seconde copie' > cptests/prénom.copie.txt$ cmp cptests/prénom.txt cptests/prénom.copie.txt$ rm -rf cptestscd -cmp pour comparer des fichiers ; elle est très utile.cp permet de copier (copy) des fichiers.cmp permet de comparer (compare) des fichiers.mv permet de déplacer (move) des fichiers.diff pour avoir plus de détails sur la différence entre deux fichiers.Une commande filtre prend un fichier en argument et renvoie une version modifiée.
$ head fichier renvoie le début d'un fichier,$ tail fichier renvoie la fin d'un fichier,$ sort fichier renvoie le fichier trié,$ cut [option] fichier sélectionne des colonnes du fichier.Ces commandes possèdent de nombreuses options ; consulter leur manuel pour les découvrir.
exercice, et y aller.animal.txt contenant une ligne : le nom d'un animal de compagnie (fictif).âge : 21 ans.head (regarder son manuel), créer un fichier nom.txt qui contient le nom de l'animal.tail (regarder son manuel), créer un fichier à_propos.txt qui reprend uniquement l'anecdote.âge.txt qui contient l'âge de l'animal sans la partie âge :, mais uniquement 21 ans dans le cas de l'exemple donné.animal.txt, âge.txt, et à_propos.txt en un fichier animal2.txt.diff pour comparer les fichiers animal.txt et animal2.txt.animal3.txt de animal.txt.animal3.txt une ligne Ceci est une copie.animal3.txt une autre ligne contenant la date et l'heure courante.cmp pour comparer les fichiers animal.txt et animal3.txt. Expliquer !animal.txt une ligne Ceci est l'original.cmp pour comparer les fichiers animal.txt et animal3.txt. Expliquer !cmp pour comparer les fichiers animal.txt et animal.txt (oui, deux fois le même fichier). Expliquer !cat
$ cat ~/somme1/essai1.py
n = int(input())
s = 0
for i in range(n):
s = s + i
print(s)
Dans cet exemple
~représente le répertoire home de l'utilisateur ; le caractère tilde s'obtient avec AltGr+2é. De là, on regarde dans le répertoiresomme1et on affiche le fichieressai1.py. Le répertoire de travail n'a pas été modifié au passage, cet exemple peut être effectué depuis tout répertoire de l'utilisateur.
$ apt install ruby ; on installe le langage de programmation ruby...$ gem install lolcat ; qui offre un gestionnaire de paquets ruby, et en particulier le logiciel lolcat$ lolcat ~/somme1/essai1.py
(( surprise ))
head, tail
On suppose que l'on est dans le répertoire somme1 qui contient le fichier essai1.py qui fait 5 lignes.
$ head -n4 essai1.py
n = int(input())
s = 0
for i in range(n):
s = s + i
$ head -n-4 essai1.py
n = int(input())
s = 0
Dans cet exemple on affiche la tête (head) du fichier, d'abord les 4 premières lignes. Puis tout sauf les 4 dernières ; la tête encore.
Sans le paramètre-nla commandeheadaffiche les 10 premières lignes par défaut.
essai1.pycmp
On suppose que l'on est dans le répertoire somme1 qui contient le fichier essai1.py.
essai1.py, faire des modifications et lancer des comparaisons.$ cp essai1.py doublon.pycmp essai1.py doubon.py ; aucun affichage signifie que les deux fichiers sont identiques.doublon.py, et ajouter un commentaire en fin de fichier # sur la ligne 6cmp indique que la comparaison s'est arrêtée sur une fin de fichier (EOF ; End Of File) et indique la ligne de la première différence, ainsi que le nombre de caractères (char) comparés.doublon.py, et remplacer la variable s par somme.cmp indique la ligne de la première différence, ainsi que le nombre de caractères (char) comparés.doublon.py, entrer Ctrl+E, on entre en mode commande, en bas l'invite de commande micro est >, on entre à la suite replace somme truc. Micro cherche toutes les occurrences de somme, il suffit de taper y pour chacune à remplacer.Exercice : essai3.py
Créer en une seule commande un fichier
essai3.pyqui en une ligne a le même fonctionnement queessai1.py.
Aide : comprendre le troisième test dans la docstring dans
essai2.py.
Redirections avec pipeline
Exercice : année
....
1. `$ mkdir calcul` 1. `$ cd calcul` 1. `$ mkdir somme1` 2. `$ cd somme1` 3. `$ nano essai1.py`
calcul puis somme1 pour notre premier test,essai1.py, par exemple :n = int(input()) s = 0 for i in range(n): s = s + i print(s)
$ python essai1.py
5 par exemple.10.$ nano essai1.py, mais on peut aussi utiliser deux fois la flèche du clavier ↑ qui remonte dans l'historique des dernières commandes.Édition de script
$ pwd
pwd signifie print working directoryhome/somme1essai1.py présent avec la commande $ lsessai1.py en essai2.py avec la commande bash $ cp essai1.py essai2.py$ lsessai2.py avec nano ou micro.def f(n): """ f retourne ... Exemples : >>> f(2) 1 >>> f(5) 10 >>> f(100) == sum(range(100)) True """ s = 0 ... n = int(input()) print(f(n))
f.essai2.py et quitter l'éditeur.$ python -m doctest -v essai2.pyCette dernière commande n'est pas à retenir, mais le principe de doctest oui !
- Penser à créer des fonctions autant que possible.
- Penser à créer une docstring pour toute fonction.
- Penser à y inclure des tests simples sous la forme :
>>> test_fonctionSortie attendue- Plus d'informations sur doctest, un module Python.