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.txt
mkdir
: 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 :
mkdir
cd
nano
ou micro
cat
Voyons 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 machin
touch
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???.jpg
pwd
? Penser à man
.-h
ou --help
, comme avec $ touch --help
touch
.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 tex
Explications :
$ 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] > fichier
Pour ajouter à un fichier l'affichage d'une commande :
$ commande [arguments] >> fichier
Dans 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.$ cat
On 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 < temporaire
Mais 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 | commande2
La 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 -a
On 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
date
dans 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.
$ time
commande [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 destination
source
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.txt
echo 'première copie' > cptests/prénom.txt
echo 'seconde copie' > cptests/prénom.copie.txt
$ cmp cptests/prénom.txt cptests/prénom.copie.txt
$ rm -rf cptests
cd -
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épertoiresomme1
et 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-n
la commandehead
affiche les 10 premières lignes par défaut.
essai1.py
cmp
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.py
cmp 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 6
cmp
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.py
qui 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/somme1
essai1.py
présent avec la commande $ ls
essai1.py
en essai2.py
avec la commande bash $ cp essai1.py essai2.py
$ ls
essai2.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.py
Cette 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_fonction
Sortie attendue
- Plus d'informations sur doctest, un module Python.