La ligne de commande Linux pour les impatient.e.s (2) : cas pratique
Tags: beginners, linux, french
Published: 2021-01-14 15:27:00

*Avant de commencer* : Il est possible que lorsqu'on lance une commande, on se trompe, souvent même d'ailleurs et moi le premier. Cependant, il existe une commande magique qui permet d'interrompre le processus en cours : `CTRL+C`. Il vous suffira d'appuyer sur la touche "contrôle" en même temps que "c" et le processus en cours sera interrompu immédiatement. On stoppe, on corrige et on recommence !

## Cut

Dans l'article précédent, nous avons pu voir comment gérer des listes à colonne unique avec `grep`. Maintenant qu'en est-il des cas où les lignes sont un peu plus denses d'informations ?

Il existe un outil simple : `cut` qui nous permettra de régler une grande partie de nos soucis.

Prenons un format comme le [csv](https://fr.wikipedia.org/wiki/Comma-separated_values) et l'exemple suivant:

```csv
Robert;Dupont;rue du Verger, 12;
"Michel";"Durand";" av. de la Ferme, 89 ";
"Michel ""Michele""";"Durand";" av. de la Ferme, 89";
"Michel;Michele";"Durand";"av. de la Ferme, 89";
```

Sauvons le directement dans un fichier avec la commande suivante :

```sh
echo 'Robert;Dupont;rue du Verger, 12;
"Michel";"Durand";" av. de la Ferme, 89 ";
"Michel ""Michele""";"Durand";" av. de la Ferme, 89";
"Michel;Michele";"Durand";"av. de la Ferme, 89";
' > exemple.csv
```

`echo` renvoie l'information que vous lui donner à l'instar d'un écho. Donc ici, `echo` enverra les informations que nous avons copiées et collées depuis la page Wikipédia. Ensuite le chevron simple (`>`) créera et remplira un nouveau fichier `exemple.csv` avec les informations que nous lui envoyons.

> Astuce : le copier coller dans le terminal fonctionne dès fois différemment en fonction des configurations. Vous pouvez coller en appuyant sur la touche majuscule en même temps que "contrôle" et "v" ou bien, en faisant clic droit et en choisissant l'option "coller".

Vérifions que le fichier n'est pas vide avec `cat exemple.csv`. Si tout va bien, les informations que nous avons collées s'y trouvent.

`cut` fonctionne avec deux options principales : `-d` pour définir le délimiteur et `-f` pour choisir la colonne que vous voulez récupérer.

Pour un premier essai, essayons : `cut -d " " -f 1`. Ici nous définissions une espace comme délimiteur et prenons le premier résultat après ce délimiteur.

```sh
cat exemple.csv | cut -d " " -f 1
```

Donne comme résultat :

```csv
Robert;Dupont;rue
"Michel";"Durand";"
"Michel
"Michel;Michele";"Durand";"av.
```

Tout s'est bien passé ! Ce sont tous les premiers résultats de la première colonne avant une espace.

> Expérimentez avec d'autres délimiteurs comme ";", "," ou "." et d'autre choix de colonnes (vous pouvez sélectionner plusieurs colonnes comme ceci : `-f 1,2`), qu'observez-vous ?

## Sed

Il est possible que vous ayez à modifier les données de votre colonne. Travailler avec les données, c'est souvent un peu de code et beaucoup de nettoyage. `sed` est une commande qui va vous permettre à remplacer des caractères selon une condition. En gros, c'est comme le chercher remplacer de n'importe quel éditeur de texte mais ici, il sera possible de l’enchaîner à notre processus.

La syntaxe de `sed` fonctionne ainsi : `sed mode/texte à changer/comment le texte doit être changé/option fichier`. Donc pour notre objectif : `sed 's/;/ /g'`. Ici nous utilisons le mode `s` qui signifie substitution ; nous remplaçons les ";" par des espaces et nous utilisons l'option `g` ce qui signifie "global" ; c'est-à-dire que nous appliquons cette condition à toutes les occurrences du fichier (**précision**: à toutes les occurrences de la ligne).

Commençons par récupérer les informations depuis notre csv :

```sh
cat exemple.csv | cut -d " " -f 1
```

Et remplaçons les ";" par une espace :

```sh
cat exemple.csv | cut -d " " -f 1 | sed 's/;/ /g'
```

Résultat :

```csv
Robert Dupont rue
"Michel" "Durand" "
"Michel
"Michel Michele" "Durand" "av.
```

Et voilà ! Rechercher, filtrer, remplacer !

> Ici, nous utilisons `sed` enchaîné avec d'autres processus, si vous désirer l'utiliser seul avec un seul fichier, il faudra utiliser l'option `-i`.  Exemple: `sed s/brusselles/Bruxelles/g fichier -i`. Cet exemple remplacera toutes les occurrences de "brusselles" dans le fichier "fichier" par son orthographe correcte : "Bruxelles".

**Précision**: l'option `-i` est utilisée pour sauvegarder la modification dans le même fichier, il est aussi possible de rediriger le résultat vers un autre fichier.

## Variables

En programmation, les variables sont des emplacements dans lesquels on peut enregistrer des informations. Par défaut, le terminal Linux utilise un langage que l'on appelle Bash ou parfois appelé shell script.

Grâce à celui-ci, nous pouvons enregistrer des informations dans des variables et les réutiliser.

Pour définir une variable, choisissez un nom, ici "nombre_deux" et une valeur, ici "2". Assigner une valeur à une variable se fait via le signe "=" : `nombre_deux=2` et puis appuyez sur Entrée.  Ensuite pour l'appeler, il faudra toujours précéder la variable par le symbole `$`. Exemple : `echo $variable_deux` et votre terminal devra vous répondre "2".

Exemple complet:

```sh
nombre_deux=2
echo $nombre_deux
```

Il est aussi possible de réassigner une variable:

```sh
nombre_deux=3
echo $nombre_deux
```

Le résultat ici devra être "3".

## Boucles

Voici un autre concept de programmation : les boucles. Les boucles permettent à un programme d'être exécuté en répétition jusqu'à arriver à une certain condition. La syntaxe de base sera comme suit :

```sh
for VARIABLE in LISTE; do ACTION $VARIABLE; done
```

Pour lire ça en français, ça sera : "pour la variable "VARIABLE" dans la liste "LISTE", faire l'action "ACTION" et, quand il n'y a plus de variable dans la liste, terminer la boucle ("done" - réalisé, en français). Vous remarquerez que la variable est appelée avec le symbole "$" comme nous avons vu plus haut et que chaque instruction est séparée par un ";".

> Vous ne devez pas nécessairement écrire les variables en capitales, je le fais juste pour différencier ce qui tient de la syntaxe de la fonction et ce que vous pouvez changer.

Essayons d'imprimer toutes les lignes de notre csv :

```sh
for ligne in $(cat exemple.csv); do echo $ligne; done
```
> Qu'est-ce `$(cat exemple.csv)` ? En utilisant `$()`, nous pouvons exécuter une autre fonction Bash à l'intérieur d'une fonction, un peu comme des poupées russes. Donc en faisant `cat exemple.csv`, nous avons imprimé le csv dans le terminal et ensuite commencé la boucle.

Devra donner ce résultat :

```
Robert;Dupont;rue
du
Verger,
12;…
"Michel";"Durand";"
av.
de
la
Ferme,
89
";…
"Michel
""Michele""";"Durand";"
av.
de
la
Ferme,
89";…
"Michel;Michele";"Durand";"av.
de
la
Ferme,
89";…
```

> Assez intéressant que le système coupe les lignes aux espaces...


Il est aussi possible de faire des boucles plus proches de ce qu'on retrouve dans les cours de maths, comme par exemple: "somme de tout n, partant de 0 jusque 100". En Bash (donc dans notre terminal), il sera possible de faire ça de la manière suivante (et en une ligne) :

```sh
somme=0; for i in $(seq 0 100); do somme=$(expr $somme + $i); done; echo $somme
```

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/iyxjzigb4wyr3an7looa.gif)

Ok, perdu.e.s ? La première fois, moi aussi. Revenons dessus pas à pas :

* `somme=0` : nous définissons une variable "somme" et l'initialisons à 0
* `;` : ce caractère nous permet de séparer différentes instructions en Bash. C'est-à-dire que nous disons à notre terminal de suivre la première instruction avec la suivante. Cela diffère de la barre verticale `|` où les deux instructions sont connectées.
* `for VARIABLE in CONDITION; do ACTION SUR VARIABLE; done` : ceci est la syntaxe de base d'une boucle en Bash.
* `$(seq 0 100)` : `seq 0 100` est une instruction affichera les chiffres de 0 à 100 dans votre terminal (essayez !).
* `expr $somme + $i` : pour que votre terminal comprenne que vous voulez faire une expression arithmétique, il faudra ajouter la fonction `expr`. Ensuite, appeler les deux variables `somme` et `i` (que nous avons définies au début de notre boucle) avec `$`. Cette fonction fera la somme de `$somme` ainsi que de `$i` et enregistra le résultat dans la variable `somme`.
* `done` : permet de finir la boucle ouverte via `for`
* `echo $somme`: va afficher le résultat final de notre boucle.

Félicitations, vous venez de faire votre première boucle !

> **Précisions**: Pour `seq`, il est aussi possible de faire `{1..100}` et arriver au même résultat. Pour `expr`, il es aussi possible de faire `$((1 + 1))`.

## Cas pratique : Fouiller une page web

Faire des additions, c'est utile mais nous avons tou.te.es des calculettes dans nos poches et franchement, on s'en fiche un peu de faire ça via le terminal.

Alors, voici un cas pratique qui va reprendre l'entièreté de la matière cet article et du précédent dans quelque chose d'utile : récupérer des informations d'une page web.

> Ici, nous allons utiliser la plupart de concepts vus dans les deux premiers articles, s'il y a des parties que vous trouvez compliquées ou peu claires, n'hésitez pas à revenir sur vos pas. Le but n'est pas de tout comprendre d'un coup tout de suite mais d'intégrer les concepts à son aise.

D'abord, prenons cette page web : [https://fr.wikipedia.org/wiki/Hedy_Lamarr](https://fr.wikipedia.org/wiki/Hedy_Lamarr).

Téléchargeons la via `wget` :

```sh
wget https://en.wikipedia.org/wiki/Hedy_Lamarr -O page.html
```

> L'option de destination du fichier est un "o" majuscule pas un zéro !

Vérifions la page :

```sh
head page.html
```

En HTML, le langage de formatage des pages web, le liens sont précédés de la balise `href=` :

```sh
grep "href=" page.html
```

Choisissons maintenant un délimiteur :

```
cut -d '"' -f 1
```

> Il est possible de faire passer plusieurs `cut` l'un à la suite de l'autre afin de filtrer au maximum le résultat de la page. En effet, le travail de filtrage de pages web peut être un peu laborieux au début afin de trouver les bons filtres. C'est important de tester différentes choses et de regarder le résultat et d'incrémenter progressivement la précision des-dits filtres.

Ici, nous allons récupérer tous les liens internes de la page Wikipédia, c'est-à-dire les liens qui commencent par `/wiki/`. Nous allons utiliser l'option `-e` de `grep` qui nous permettra de faire passer un filtre spécial (une expression régulière ou *regex*).

> Les [*regex*](https://fr.wikipedia.org/wiki/Expression_r%C3%A9guli%C3%A8re) sont une langage de programmation qui permet de faire du filtrage plus précis de texte. Ce n'est pas le sujet de cet article car c'est assez abscon au début. Retenez ici jusque le "^" défini le début d'une chaine de caractère. Donc, dans l'exemple suivant `^/wiki/` signifie : toutes les lignes qui commencent exclusivement par "/wiki/".

```sh
grep "href=" page.html | cut -d '"' -f 2 | grep -e "^/wiki/" | sort -u > liens.txt
```

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/6slvou22f5j0h4zrvqp9.gif)

Vous remarquerez que les liens ne sont pas complets, c'est pour cela que nous allons corriger cela avec `sed`  et l'option `-i` :

```sh
sed 's/\/wiki\//https:\/\/fr\.wikipedia\.org\/wiki\//g' liens.txt -i
```

> Pourquoi des `\` devant les caractères spéciaux ? Car `sed` fonctionne avec des *regex* et chaque caractère à une signification et une fonction particulière. Nous voulons considérer ces caractères de façon littérale, donc nous devons *les échapper* avec le caractère `\`. C'est-à-dire, dire à `sed` qu'il ne faut pas les considérer comme des fonctions mais comme de simples caractères. C'est le cas pour les `/` et `.`. Si vous regardez bien, les `/` qui se sont pas échappés, ce sont ceux qui sont nécessaires pour la syntaxe de `sed`.
> **Astuce** : Il est aussi possible d'utiliser `#` lorsqu'il y a beaucoup de caractères à échapper. Donc, ceci est valide aussi et donnera le même résultat : `sed 's#/wiki//#https://fr.wikipedia/.org/wiki/#' liens.txt -i` (et est un peu plus facile à lire).

Vérifions notre liste de liens :

```sh
head liens.txt
```

Maintenant, nous allons télécharger tous les liens les uns à la suite de l'autre pour une lecture hors-ligne confortable.

```sh
mkdir dossier_liens
for lien in $(cat liens.txt); do wget $lien -P dossier_liens; done
```

> L'option `-P` permet d'envoyer le résultat de `wget` dans un dossier plutôt que dans un fichier.

Et voilà le travail !

> Il est possible de faire tout cela en une seule ligne. Les fameux "one-liners" dont les développeur.euse.s raffolent tant. Essayer de trouver une manière de faire ces commandes en une seule ligne et lancez-la ! Essayez d'arriver au même résultat avec différentes pages Wikipédia.

# Conclusion

Nous avons vu dans ces deux articles comment traiter des listes, les filtrer, les nettoyer et en refaire des objets que nous pouvons utiliser par la suite !

N'hésitez pas à me contacter si vous avez des questions ou des remarques.

**Edit** : Merci à Dattaz pour les précisions et les astuces !