Linux LPIC-1 (101-500) #2 – Edition de fichiers et ligne de commande

Linux LPIC-1 (101-500) #2 – Edition de fichiers et ligne de commande

A plant in the middle of a dark forest

Edition de fichiers

Nano

nano c’est un peu l’équivalent de notepad.exe sous Windows : il est simple d’usage, pas très puissant et, omniprésent.

nano présente une interface simple et épurée qui lui permet de s’afficher correctement même avec une résolution limitée (t’as connu les écrans cathodiques 14 » avec une résolution de 640×480 ?).

Les raccourcis clavier sont affichés en bas de l’écran, plus l’écran est large plus il y en a qui s’affichent. Ils peuvent paraître cryptiques pendant 30 ou 40 secondes : mais pourquoi les raccourcis commencent par un accent circonflexe ?!
Ca ne s’invente peut-être pas mais ça correspond à Ctrl. Pour capter, fais un Ctrl + c dans un shell (ça permet d’arrêter l’exécution d’une commande, et ça fonctionnera même si tu n’as pas de commande en cours).

D’autre part, les raccourcis sont inscrits en majuscule mais sont insensibles à la casse.

L’exemple ci-dessous a été réalisé par un cascadeur professionnel un peu naze, ne pas tenter de reproduire.

Bash
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Wed Dec 18 03:34:13 2024 from 10.0.0.2
root@debian:~# rm -rf /^C
root@debian:~#

Ctrl + c a été lancé (ligne 4) et le prompt est revenu vide à la ligne suivante.

Certains raccourcis utilisent Alt, dans ce cas ils sont préfixé, non plus de ^, mais de M (je sais pas, c’est peut-être pour meta ?).

Lorsque l’on quitte nano, avec Ctrl + x, il nous est proposé de nommer le fichier. Si tu éditais un fichier, tu valides par entrée si tu souhaites écrire dans le fichier initial ou, tu changes le nom si tu souhaites en faire un nouveau.

nano offre la possibilité d’ouvrir plusieurs fichiers avec Ctrl + r. Par défaut, il va insérer le contenu du second fichier dans le premier mais tu peux lui demander spécifiquement de l’ouvrir dans un autre tampon (buffer) à l’aide de Alt + f.
On naviguera entre les tampons à l’aide de Alt + < et Alt + >.
Si tu souhaites créer un nouveau fichier vide dans cet autre tampon, il ne faudra pas le nommer lors de la commande Ctrl + r, Alt + f. nano va de facto créer un fichier vide dans un nouveau buffer. Tu pourras le nommer à l’enregistrement.

Ctrl + k permet de couper, par défaut il coupera la ligne entière qui entrera alors dans le presse-papier pour être collée ailleurs. Mais on peut faire des opérations plus précises en marquant une région : on positionne le curseur au début de la région à marquer puis on presse Alt + a et on se déplace avec les flèches vers la fin de la zone souhaitée (on peut sélectionner ainsi plusieurs lignes) et là on peut couper avec Ctrl + k.
Je ne sais pas trop ce qui est passé par la tête des dévs mais, pour copier il faut utiliser Alt + ^. Non non ! Pas celui à gauche de P ; celui du Alt gr + ç. Ca donne pour copier (une fois la région sélectionnée) : Alt + Alt gr + ç.

Pour rechercher et remplacer du texte, dans nano on utilisera les raccourcis :

  • Ctrl + w pour rechercher simplement (la recherche est insensible à la casse), on pourra lancer le même raccourci et valider par entrée (nano conserve le terme rechercher sauf si on tape un nouveau) ou Alt + w pour t’éviter de valider de nouveau. Attention, la recherche bouclera dans le fichier après être arrivée à la fin (juste au dessus de la liste des raccourcis en bas de l’écran).
  • Ctrl + \ (encore une purge à taper sur nos claviers AZERTY) et Alt + r permettent de rechercher-remplacer (la recherche accepte les expressions régulières avec Alt + r… oué oué encore une fois… j’adore nano). On donne alors le terme recherché et ce par quoi on veut le remplacer. nano va nous positionner sur chacune des instances pour nous demander si on remplace sauf si on lui répond A pour toutes les instances du terme recherché.

Vim

Sur une machine installée avec le minimum de paquet, vi sera fort probablement installé mais pas vim (Vi IMproved) donc apt install vim. Lorsqu’il est installé, il répondra aux deux noms lorsque l’on souhaite l’ouvrir alors on ne parlera plus que de vivim est très austère déroutant de prime abord parce que son mode par défaut, le mode normal, ne permet pas de modifier.

C’est loin d’être l’éditeur de texte le plus intuitif… Il faudra consacrer un peu de temps pour réussir à l’utiliser sans réfléchir, même pour les tâches du quotidien. Mais après quelques jours d’utilisation, tu t’en sorts très bien et tu commences à toucher du doigt sa puissance.

Je t’invite encore une fois à lire l’excellent Vim pour les humains – oué je sais, je te l’ai déjà dit dans le précédent chapitre mais comme tu l’as pas lu entre temps – du coup je vais te donner quelques raccourcis clavier pour t’en sortir vite fait parce que là c’est pour toi que je l’écris, pas pour moi :

  • échap te permet de revenir au mode normal/commande,
  • i mode insertion (là où ton curseur se trouve),
  • a mode ajout (après la position de ton curseur),
  • y pour yank (l’arrachement) te permet de copier un mot et yy te permet de copier une ligne,
  • cw pour cut word,
  • u pour undo (annuler),
  • dd pour supprimer une ligne (5dd pour supprimer 5 lignes ou 19-22dd pour supprimer les lignes 19 à 22),
  • p pour paste (coller), P pour coller à la ligne du dessus,
  • / pour rechercher (puis n et N pour rechercher les autres occurrences en avant et en arrière),
  • o pour ajouter une ligne en dessous et O pour l’ajouter au dessus.
  • : pour lancer une commande.
    • :w pour enregistrer,
    • :q pour quitter,
    • :q! pour quitter sans enregistrer,
    • :wq pour enregistrer et quitter (plus rapide : x),
    • :3 pour te rendre à la ligne 3,
  • Pour rechercher et remplacer en voulant vérifier un truc j’ai trouvé cet article là, ça te plaira.

La ligne de commande

Personnaliser Bash

La personnalisation de Bash est gérée à deux niveaux : globalement pour le système et localement pour l’utilisateur.

Au niveau du système cela se passe dans le fichier /etc/bash.bashrc et le dossier /etc/profile.d/ mais voyons plutôt ce qu’il se passe au niveau de l’utilisateur.

Par défaut, ls n’affiche pas les dotfiles ; ces fichiers de configuration dont le nom commence par un point. On peut néanmoins lui demander de les afficher spécifique à l’aide de l’argument all tel que ls -a. Dans ton dossier personnel, trois fichiers de configuration vont nous intéresser : .bash_history, .bashrc, et, .profile.

Le premier, .bash_history, contient l’historique des commandes lancées et l’on peut y accéder directement à l’aide de la commande history. Je ne sais pas si on y reviendra alors je te précise que tu peux faire une recherche dans ton historique à l’aide de Ctrl + r, il faudra taper une chaîne significative pour retrouver la ou les occurrences de cette chaîne dans les commandes. Si le premier résultat trouvé n’est pas le bon, tu peux appuyer de nouveau sur Ctrl + r pour revenir plus en arrière.

.bashrc a un rôle bien plus important. C’est notamment là que l’on va pouvoir agir sur la manière dont l’historique est enregistré (longueur d’historique enregistrée par exemple), vérifier que le shell est en couleur, définir le prompt (root@debian:/arbo/rescence/ comme dans la copie d’écran ci-dessus) et même, des alias de commandes et des fonctions.

.profile va (notamment) charger le fichier .bashrc pour les sessions de shell interactives.

Revenons sur .bashrc, les alias et les fonctions. Tu as du remarquer que selon le système sur lequel tu es ou l’utilisateur que utilises, ls n’est pas toujours coloré ? C’est normal : certains systèmes génèrent des alias activés par défaut dont celui de ls afin qu’ils te paraissent colorés par défaut.

/home/antoine/.bashrc
[...]
# enable color support of ls and also add handy aliases
if [ -x /usr/bin/dircolors ]; then
    test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
    alias ls='ls --color=auto'
    #alias dir='dir --color=auto'
    #alias vdir='vdir --color=auto'

    #alias grep='grep --color=auto'
    #alias fgrep='fgrep --color=auto'
    #alias egrep='egrep --color=auto'
fi
[...]

Mais tu peux tout à fait créer tes propres alias.

/home/antoine/.bashrc
[...]
alias radio404='cvlc https://www.radioking.fr/play/radio404 --intf=rc'
alias tambouille='cvlc https://lagrossetambouille.com/flux --intf=rc'
[...]

Et même des fonctions et là je reprends directement celle du cours dans mon .bashrc pour l’ajouter directement après mes alias.

Bash
antoine@dressing:~$ vi ~/.bashrc
/home/antoine/.bashrc
[...]
alias radio404='cvlc https://www.radioking.fr/play/radio404 --intf=rc'
alias tambouille='cvlc https://lagrossetambouille.com/flux --intf=rc'

cheat() { curl "https://cheat.sh/$1"; }
[...]

Ok c’est parti, j’enregistre et je lance !

Bash
antoine@dressing:~$ cheat iptables
bash: cheat : commande introuvable
antoine@dressing:~$ ah merde
bash: ah : commande introuvable

# Il faut recharger le fichier .bashrc

antoine@dressing:~$ source .bashrc
antoine@dressing:~$ cheat iptables
 cheat:iptables 
---
tags: [ networking ]
---
# To show hit for rules with auto refresh:
watch --interval 0 'iptables -nvL | grep -v "0     0"'

# To show hit for rule with auto refresh and highlight any changes since the last refresh:
watch -d -n 2 iptables -nvL

# To block port 902 and hide this port from nmap:
iptables -A INPUT -i eth0 -p tcp --dport 902 -j REJECT --reject-with icmp-port-unreachable
[...]

On peut également définir des variables dans notre .bashrc qui seront chargées automatiquement lors de l’ouverture de sessions shell.

/home/antoine/.bashrc
[...]
alias radio404='cvlc https://www.radioking.fr/play/radio404 --intf=rc'
alias tambouille='cvlc https://lagrossetambouille.com/flux --intf=rc'

cheat() { curl "https://cheat.sh/$1"; }
BALAISE="un gros noob"
[...]
Bash
antoine@dressing:~$ source .bashrc
antoine@dressing:~$ echo "Regarde comme je suis $BALAISE"
Regarde comme je suis un gros noob

La variable $BALAISE n’est accessible que dans mon shell Bash. Si je souhaite que les processus enfants de mon shell (scripts, sous-shell, programmes) il faudra que je la précède de la mention export (ou que je le fasse en deux lignes).

A titre personnel, je profite de mon .bashrc pour lancer mon script de chargement de ma clé privée dans mon agent ssh qui lance à son tour mon gitpuller.sh si la clé n’était pas encore chargée.

Gestion de fichiers

Encore une fois, c’est un point que j’ai déjà traité par le passé. Toutefois, je ne t’y avais pas parlé de deux outils suivants pourtant si pratiques.

head permet d’afficher à l’écran les 10 premières lignes d’un fichier quand tail permet d’afficher les 10 dernières. Tout deux prennent le paramètre -n si l’on souhaite spécifier le nombre de lignes que l’on souhaite afficher.

tail prend également le paramètre -f (follow) pour afficher la fin du fichier et toute nouvelle entrée à la fin du fichier. Dans ce cas, tail reste actif et ne te rends pas la main (il faudra Ctrl + C pour l’arrêter. Tu peux cependant sauter des lignes avec entrée pour t’y retrouver plus facilement, ça ne modifiera pas le contenu du fichier source.

On peut évidemment aller plus loin en commençant d’afficher après n lignes… etc.

Redirection de données avec pipe

On peut être amené à vouloir utiliser la sortie (output) d’une première commande en entrée d’une autre (input). Par défaut, Linux va recevoir l’entrée d’une commande par son entrée standard (stdin) et envoyer le résultat d’une commande vers la sortie standard (stdout).

En séparant une première commande d’une seconde à l’aide du caractère pipe, on va pouvoir rediriger la sortie standard (stdout) de la première commande vers l’entrée standard (stdin) de la seconde.

Bash
commande1 | commande2

Si l’on prend un cas concret, en console dmesg retourne une quantité de lignes supérieure à la taille de l’écran empêchant de lire la grande majorité des lignes. En redirigeant la sortie de dmesg vers less tu pourras parcourir toutes les lignes, aller en avant et arrière et, même faire des recherches.

Bash
dmesg | less

Les commandes récentes ont tendance a être compatibles avec l’usage de pipe mais pour les commandes les plus anciennes ce n’est pas toujours le cas. mkdir par exemple n’aime pas du tout recevoir une liste de dossier à créer depuis pipe.

Pour ce genre de cas, tu pourras utiliser xargs afin d’itérer sur les entrées.

Bash
root@debian:~# FOLDERS="toto tata titi"
root@debian:~# echo $FOLDERS | mkdir
mkdir: opérande manquant
#Saisissez « mkdir --help » pour plus d'informations.
root@debian:~# echo $FOLDERS | xargs mkdir
root@debian:~# ls
tata  titi  toto
root@debian:~# ls -l
total 12
drwxr-xr-x 2 root root 4096 24 déc.  21:25 tata
drwxr-xr-x 2 root root 4096 24 déc.  21:25 titi
drwxr-xr-x 2 root root 4096 24 déc.  21:25 toto

Redirection de données avec Redirects

pipe permet de rediriger stdout vers une autre commande alors que redirects permet de rediriger ces données vers une destination (fichier ou périphérique). Je redirigeais la sortie de tar vers un lecteur de bande il y a quelques années mais je n’ai plus de serveur qui traîne dans mon salon alors on va surtout voir les redirections vers des fichiers.

Pour rediriger la sortie d’une commande vers un fichier on utilise le caractère plus grand que. Disons que je veuille enregistrer la date et l’heure dans un fichier texte et le contenu de ma home.

Bash
root@debian:~# date > root_home_content.txt
root@debian:~# ls -l > root_home_content.txt
root@debian:~# cat root_home_content.txt 
total 12
-rw-r--r-- 1 root root    0 24 déc.  22:05 root_home_content.txt
drwxr-xr-x 2 root root 4096 24 déc.  21:25 tata
drwxr-xr-x 2 root root 4096 24 déc.  21:25 titi
drwxr-xr-x 2 root root 4096 24 déc.  21:25 toto
root@debian:~# 

root_home_content.txt contient alors le résultat de la commande ls mais pas la date : la seconde redirection a écrasé le contenu du fichier. Pour ajouter du contenu dans un fichier sans écraser le contenu déjà présent, on va doubler le plus grand que.

Bash
root@debian:~# date > root_home_content.txt
root@debian:~# ls -l >> root_home_content.txt
root@debian:~# cat root_home_content.txt 
mar. 24 déc. 2024 22:07:18 CET
total 16
-rw-r--r-- 1 root root    0 24 déc.  22:05 root_home_content.txt
drwxr-xr-x 2 root root 4096 24 déc.  21:25 tata
drwxr-xr-x 2 root root 4096 24 déc.  21:25 titi
drwxr-xr-x 2 root root 4096 24 déc.  21:25 toto
root@debian:~# 

Comme tu le vois, le contenu du fichier a changé mais surtout : le contenu initial a été remplacé. Si dans certains cas c’est pratique de remplacer le contenu d’un fichier, c’est pas pour autant toujours idéal.

De même que tu peux rediriger la sortie d’une commande vers un fichier, tu peux rediriger le contenu d’un fichier vers l’entrée d’une commande avec plus petit que.

Bash
root@debian:~# sort < root_home_content.txt 
drwxr-xr-x 2 root root 4096 24 déc.  21:25 tata
drwxr-xr-x 2 root root 4096 24 déc.  21:25 titi
drwxr-xr-x 2 root root 4096 24 déc.  21:25 toto
mar. 24 déc. 2024 22:07:18 CET
-rw-r--r-- 1 root root   32 24 déc.  22:07 root_home_content.txt
total 16
root@debian:~# 

Mais Antoine, sort aurai pu fonctionner sans le < !

Oui je sais mais c’était pour l’exemple alors accepte le stp.

Bon j’ai pas de périphérique physique vers lequel je peux rediriger mais j’ai quand même quelque chose pour toi : /dev/null. C’est un périphérique virtuel qui sert de trou noir pour les sorties de commande.

Bash
root@debian:~# ls -l > /dev/null
root@debian:~# 

Génial non ? ls n’a même pas moufté. Bon je déconne, il a un réel intérêt regarde. Si je recherche tous les fichiers de tableur de ma home tout va bien.

Bash
antoine@dressing:~$ find ~/ -name *.xls
/home/antoine/Téléchargements/status(1).xls
/home/antoine/Téléchargements/status.xls
antoine@dressing:~$ 

Mais lorsque je liste tous ceux du disque dur là c’est déjà plus problématique ; ça dépasse même le buffer de mon terminal alors essayer de retrouver mes tableurs dans tous ça, j’te raconte pas l’angoisse.

Bash
antoine@dressing:~$ find / -name *.xls
[...]
find: ‘/proc/9275/task/9275/fdinfo’: Permission non accordée
find: ‘/proc/9275/task/9275/ns’: Permission non accordée
find: ‘/proc/9275/task/9285/fd’: Permission non accordée
find: ‘/proc/9275/task/9285/fdinfo’: Permission non accordée
find: ‘/proc/9275/task/9285/ns’: Permission non accordée
find: ‘/proc/9275/task/9286/fd’: Permission non accordée
find: ‘/proc/9275/task/9286/fdinfo’: Permission non accordée
find: ‘/proc/9275/task/9286/ns’: Permission non accordée
find: ‘/proc/9275/task/9287/fd’: Permission non accordée
[...]

Jusqu’ici je t’ai parlé de stdin et de stdout mais il existe également également un canal d’erreur standard : stderr. Et chacun porte un numéro : stdin porte le 0, stdout porte le 1 et, stderr porte le 2.

On va rediriger stderr vers /dev/null afin de ne plus l’afficher.

Bash
antoine@dressing:~$ find / -name *.xls 2>/dev/null
/usr/share/doc/libole-storage-lite-perl/examples/test.xls
/home/antoine/Téléchargements/status(1).xls
/home/antoine/Téléchargements/status.xls
antoine@dressing:~$ 

Et là c’est bien plus lisible non ?

Filtrage des sorties avec sort et cut

sort est pratique mais par défaut il est limité. Lorsque l’on fait une ls -l que l’on redirige la sortie vers sort, ce dernier n’a aucune idée que nous, humains, voyons des colonnes parce que le délimiteur est variable. Un coup c’est 1 espace, un coup c’est 2… Bref, il est pas aidé.

Bash
antoine@dressing:~$ ls -l | sort -r
total 432
-rw-r--r--  1 antoine antoine   3736 11 avril  2024 gandi_dns_updater.sh
-rw-r--r--  1 antoine antoine 355339 20 déc.  17:00 fin_de_la_bande.png
-rw-r--r--  1 antoine antoine   3282 10 juil. 17:20 test.csv
drwxr-xr-x  9 antoine antoine   4096  9 juil. 12:38 test
drwxr-xr-x  4 antoine antoine  20480 24 déc.  08:10 Téléchargements
drwxr-xr-x  2 antoine antoine   4096  9 févr.  2024 Public
drwxr-xr-x  2 antoine antoine   4096  9 févr.  2024 Musique
drwxr-xr-x  2 antoine antoine   4096  9 févr.  2024 Modèles
drwxr-xr-x  2 antoine antoine   4096  6 nov.  11:16 testparser
drwxr-xr-x  2 antoine antoine   4096 24 déc.  13:31 Vidéos
drwxr-xr-x  2 antoine antoine   4096 24 déc.  13:30 Images
drwxr-xr-x  2 antoine antoine   4096 20 déc.  08:01 Bureau
drwxr-xr-x 16 antoine antoine   4096 27 nov.  14:32 VirtualBox VMs
drwxr-xr-x 10 antoine antoine   4096 24 déc.  13:30 Documents

C’est en fin de compte assez compréhensible : t’as déjà essayé de ranger tes photos en les nommant au format « jour-mois-année » ? Bah les premiers jours de chaque premier mois de chaque année se retrouvent ensemble. Bah là c’est pareil, sort va agir depuis le début de la ligne jusqu’à la fin et classer de façon alphabétique (inverse dans notre cas avec -r).

Il va donc falloir préparer un peu notre sortie de ls pour sort. On va pouvoir faire ça avec tr pour trim (tailler) afin de squeeze (remplacer), les espaces par un délimiteur de notre choix. On pourrait utiliser la virgule comme délimiteur mais ça rend notre sortie écran peu lisible. On va plutôt utiliser une tabulation (attention, pour taper une tabulation dans la console, il faudra feinter en utilisant Ctrl + v + Tab).

Maintenant que le séparateur entre les colonnes est fixe, on va indiquer à sort le délimiteur ainsi que la colonne qui nous intéresse en l’occurrence la 9ème et dernière colonne.

Bash
antoine@dressing:~$ ls -l | tr -s " " "      " | sort -r -t "        " -k 9
drwxr-xr-x      16      antoine antoine 4096    27      nov.    14:32   VirtualBox      VMs
drwxr-xr-x      2       antoine antoine 4096    24      déc.    13:31   Vidéos
drwxr-xr-x      2       antoine antoine 4096    6       nov.    11:16   testparser
-rw-r--r--      1       antoine antoine 3282    10      juil.   17:20   test.csv
drwxr-xr-x      9       antoine antoine 4096    9       juil.   12:38   test
drwxr-xr-x      4       antoine antoine 20480   24      déc.    08:10   Téléchargements
drwxr-xr-x      2       antoine antoine 4096    9       févr.   2024    Public
drwxr-xr-x      2       antoine antoine 4096    9       févr.   2024    Musique
drwxr-xr-x      2       antoine antoine 4096    9       févr.   2024    Modèles
drwxr-xr-x      2       antoine antoine 4096    24      déc.    13:30   Images
-rw-r--r--      1       antoine antoine 3736    11      avril   2024    gandi_dns_updater.sh
-rw-r--r--      1       antoine antoine 355339  20      déc.    17:00   fin_de_la_bande.png
drwxr-xr-x      10      antoine antoine 4096    24      déc.    13:30   Documents
drwxr-xr-x      2       antoine antoine 4096    20      déc.    08:01   Bureau
total   432

Alors tu as peut-être déjà vu la limite : mon dossier « VirtualBox VMs » qui a un espace. Mais je vais lâchement l’ignorer… D’ailleurs tu feras attention, dans les commandes ci-dessus mes tabulations sont remplacées par des espaces multiples.

Cette fois-ci, c’est bien classé par ordre alphabétique inverse. Imaginons alors que seules les colonne de nom et de taille m’intéressent à la manière de du, on utilise alors cut.

Bash
antoine@dressing:~$ ls -l | tr -s " " "      " | sort -r -t "        " -k 9 | cut -d "       " -f 9,5
4096    VirtualBox
4096    Vidéos
4096    testparser
3282    test.csv
4096    test
20480   Téléchargements
4096    Public
4096    Musique
4096    Modèles
4096    Images
3736    gandi_dns_updater.sh
355339  fin_de_la_bande.png
4096    Documents
4096    Bureau

antoine@dressing:~$ 

T’as remarqué la ligne vide ? C’est la ligne de total de ls. Elle n’a pas de 5ème et 9ème colonne donc elle est vide. La seconde partie du nom du dossier ‘VirtualBox VMs » a également disparu.

Tu peux aussi parfaitement souhaiter avoir le résultat d’une commande à l’écran mais aussi dans un fichier. Pour ça il suffit de pipe une commande vers tee et le nom du fichier souhaité.

Bash
antoine@dressing:~$ ls -l | tr -s " " "      " | sort -r -t "        " -k 9 | cut -d "       " -f 9,5 | tee /tmp/ma_home.txt
4096    VirtualBox
4096    Vidéos
4096    testparser
3282    test.csv
4096    test
20480   Téléchargements
4096    Public
4096    Musique
4096    Modèles
4096    Images
3736    gandi_dns_updater.sh
355339  fin_de_la_bande.png
4096    Documents
4096    Bureau

antoine@dressing:~$ cat /tmp/ma_home.txt
4096    VirtualBox
4096    Vidéos
4096    testparser
3282    test.csv
4096    test
20480   Téléchargements
4096    Public
4096    Musique
4096    Modèles
4096    Images
3736    gandi_dns_updater.sh
355339  fin_de_la_bande.png
4096    Documents
4096    Bureau

antoine@dressing:~$ 

Filtrage des sorties avec grep

grep (Globally search a Regular Expression and Print) est un outil de recherche utilisant des motifs de type expression régulière (on nommera ça regex ou regexp) comme terme de recherche. grep peut rechercher autant dans des fichiers que dans la sortie d’une commande.

On peut faire varier le type de motif utilisé par grep soit en ajoutant un argument, soit en utilisant un alias. Ainsi pour utiliser un motif de type Extended regexp on utilisera au choix egrep ou grep -E, pour un motif de chaîne on utilisera fgrep ou grep -F ou encore grep tout simplement (les alias ne sont pas toujours inclus dans les distributions).

Les expressions régulières permettent d’implémenter des motifs identiques quelque soit le système ou le langage de programmation utilisé. Elles permettent de définir des critères très précis et sont très puissantes mais en aucun cas intuitives. Tu pourras te faire une première idée de ce que c’est à l’aide du manuel man 7 regex (on utilise man 7 parce que regex n’est pas une commande mais un standard).

Voyons quelques exemples d’usage de grep. Extrayons d’abord toutes les lignes de gitpuller.sh contenant le mot « local ».

Attention, grep est sensible à la casse.

Bash
antoine@dressing:~/scripts$ cat gitpuller.sh | grep "Local"
antoine@dressing:~/scripts$
Bash
antoine@dressing:~/scripts$ cat gitpuller.sh | grep local
local_repository_folder=$HOME"/GIT-LAB/"
REPOS=($(find $local_repository_folder -name ".git" -type d))
        local -r progress=${1?"progress is mandatory"}
        local -r total=${2?"total elements is mandatory"}
        local -r info=${3:-"In progress"}
        # function local variables
        local progress_segment
        local todo_segment
        local info_segment=""
        local compl_segment=""
        local -r progress_ratio=$((progress * 100 / total))
        local -r bar_size=$((COLUMNS - ${#info_segment} - ${#compl_segment}))
        local -r progress_segment_size=$((bar_size * progress_ratio / 100))
        local -r todo_segment_size=$((bar_size - progress_segment_size))

Bon c’est pas mal mais le commentaire au milieu me dérange alors on va essayer les regexp et afficher les lignes qui commencent par « local ». Pour cela on va utiliser ^ qui représente le début de ligne (là où $ représente la fin de ligne).

Bash
antoine@dressing:~/scripts$ cat gitpuller.sh | egrep "^local"
local_repository_folder=$HOME"/GIT-LAB/"
antoine@dressing:~/scripts$ 

Et oui, mes autre lignes de définition de variables locales à la fonction ne sont pas en début de ligne mais après un nombre variable d’espaces. On utilisera alors \s pour indiquer un espace et * pour indiquer « peut importe le nombre ».

Bash
antoine@dressing:~/scripts$ cat gitpuller.sh | egrep "^\s*local"
local_repository_folder=$HOME"/GIT-LAB/"
        local -r progress=${1?"progress is mandatory"}
        local -r total=${2?"total elements is mandatory"}
        local -r info=${3:-"In progress"}
        local progress_segment
        local todo_segment
        local info_segment=""
        local compl_segment=""
        local -r progress_ratio=$((progress * 100 / total))
        local -r bar_size=$((COLUMNS - ${#info_segment} - ${#compl_segment}))
        local -r progress_segment_size=$((bar_size * progress_ratio / 100))
        local -r todo_segment_size=$((bar_size - progress_segment_size))
antoine@dressing:~/scripts$

Dans gitpuller.sh j’ai défini mes variables internes en majuscules. Je pourrais vouloir les afficher également alors, il va falloir faire un ou (ici le pipe) entre les lignes qui commencent par local et celles qui sont en majuscules (entre crochet, je donne les lettres de A à Z).

Bash
antoine@dressing:~/scripts$ cat gitpuller.sh | egrep "^\s*local|^\s*[A-Z]"
local_repository_folder=$HOME"/GIT-LAB/"
REPOS=($(find $local_repository_folder -name ".git" -type d))
PULL_ITER=${#REPOS[@]}
PROGRESS_BAR_CHAR=( )
PROGRESS_BAR_INFO_TEMPLATE=' %s [%2d/%2d] '
PROGRESS_BAR_COMPL_TEMPLATE=' %2d%% '
PROGRESS_BAR_TEMPLATE='\033[1m%s%s\033[0m%s%s\r'
PROGRESS_BAR_DISPLAY_INFO=1
PROGRESS_BAR_DISPLAY_COMPL=1
        SECONDS=0
        local -r progress=${1?"progress is mandatory"}
        local -r total=${2?"total elements is mandatory"}
        local -r info=${3:-"In progress"}
        local progress_segment
        local todo_segment
        local info_segment=""
        local compl_segment=""
        local -r progress_ratio=$((progress * 100 / total))
        local -r bar_size=$((COLUMNS - ${#info_segment} - ${#compl_segment}))
        local -r progress_segment_size=$((bar_size * progress_ratio / 100))
        local -r todo_segment_size=$((bar_size - progress_segment_size))
        COLUMNS=$(tput cols)
        COLUMNS=$(tput cols)
antoine@dressing:~/scripts$ 

Mais j’aurai pu ne demander que les mots qui commencent par P, R, O, G, E et S.

Bash
antoine@dressing:~/scripts$ cat gitpuller.sh | egrep "^\s*local|^\s*[P,R,O,G,E,S]"
local_repository_folder=$HOME"/GIT-LAB/"
REPOS=($(find $local_repository_folder -name ".git" -type d))
PULL_ITER=${#REPOS[@]}
PROGRESS_BAR_CHAR=( )
PROGRESS_BAR_INFO_TEMPLATE=' %s [%2d/%2d] '
PROGRESS_BAR_COMPL_TEMPLATE=' %2d%% '
PROGRESS_BAR_TEMPLATE='\033[1m%s%s\033[0m%s%s\r'
PROGRESS_BAR_DISPLAY_INFO=1
PROGRESS_BAR_DISPLAY_COMPL=1
        SECONDS=0
        local -r progress=${1?"progress is mandatory"}
        local -r total=${2?"total elements is mandatory"}
        local -r info=${3:-"In progress"}
        local progress_segment
        local todo_segment
        local info_segment=""
        local compl_segment=""
        local -r progress_ratio=$((progress * 100 / total))
        local -r bar_size=$((COLUMNS - ${#info_segment} - ${#compl_segment}))
        local -r progress_segment_size=$((bar_size * progress_ratio / 100))
        local -r todo_segment_size=$((bar_size - progress_segment_size))
antoine@dressing:~/scripts$ 

Ou encore ne m’intéresser qu’aux lignes qui, en dehors des espaces, commencent par 5 caractères en majuscule suivi de =.

Bash
antoine@dressing:~/scripts$ cat gitpuller.sh | egrep "^\s*[A-Z]{5}="
REPOS=($(find $local_repository_folder -name ".git" -type d))
antoine@dressing:~/scripts$ 

Pour info, l’usage de cat avant le pipe est pratique visuellement mais on peut tout à fait donner le fichier directement à grep ce qui donne le même résultat.

Bash
antoine@dressing:~/scripts$ egrep "^\s*[A-Z]{5}=" gitpuller.sh
REPOS=($(find $local_repository_folder -name ".git" -type d))
antoine@dressing:~/scripts$ 

On peut aussi faire cette recherche sur l’ensemble des fichiers du dossier.

Bash
antoine@dressing:~/scripts$ rgrep -E "^\s*[A-Z]{5}=" .
./abuseipchecker.sh:    OWNER=$(whois $IP_TO_CHECK)
./gitpullerdemo.sh:REPOS=($(find $local_repository_folder -name ".git" -type d))
./gitpuller.sh:REPOS=($(find $local_repository_folder -name ".git" -type d))
antoine@dressing:~/scripts$ 

Les sommes de contrôle

Lorsque l’on échangeait des données à l’aide de connexion RTC ou que l’on stockait des données sur des supports magnétiques (disquettes, bandes) il était fréquent que les données soient corrompues.

Pour vérifier que les données n’ont pas été altérées entre l’émetteur et le récepteur, on peut utiliser les sommes de contrôles (checksum) : on applique un algorithme mathématique à un fichier pour produire un nombre. Un moyen simple de savoir de quels algorithme tu disposes, tu peux lister les fichiers terminant par sum dans le dossier /usr/bin.

Bash
antoine@dressing:~$ ls /usr/bin/*sum
/usr/bin/b2sum  /usr/bin/cksum  /usr/bin/md5sum  /usr/bin/sha1sum  /usr/bin/sha224sum  /usr/bin/sha256sum  /usr/bin/sha384sum  /usr/bin/sha3sum  /usr/bin/sha512sum  /usr/bin/shasum  /usr/bin/sum
antoine@dressing:~$ 

Les binaires /usr/bin/shannnsum sont des alias de /usr/bin/shasum.

Plusieurs problématiques se présentent lors du choix de l’algorithme : est-il fiable ou des pirates l’ont-ils percé ? Est-il disponible sur la machine du destinataire ? Est-il rapide ou très demandeur de ressources ?

md5 et sha1 sont considérés comme percés, il est déconseillé de les utiliser car un attaquant pourrait rendre la somme de contrôle identique tout en ayant altéré les données.

Idéalement, plus le nombre dans l’alias de shasum est élevé mieux c’est. Cependant il n’est ni certain que la même valeur soit présente chez le récepteur et surtout plus ce sera élevé et plus cela pourrait prendre de temps à calculer.

Bash
antoine@dressing:~$ ALGO="sha3sum sha224sum sha256sum sha384sum sha512sum"
for OSEF in ${ALGO}; do echo $(time $OSEF CentOS-7-x86_64-NetInstall-2009.iso); done

real    0m1,321s
user    0m1,249s
sys     0m0,072s
088dcf34fd055b995e4ec613e26edcab757bcd7449d8427018b7c59e CentOS-7-x86_64-NetInstall-2009.iso

real    0m1,247s
user    0m1,202s
sys     0m0,044s
3da75adf68759dff05f5180397241f32f17f4178a03ea41012b0cd03 CentOS-7-x86_64-NetInstall-2009.iso

real    0m1,250s
user    0m1,202s
sys     0m0,048s
b79079ad71cc3c5ceb3561fff348a1b67ee37f71f4cddfec09480d4589c191d6 CentOS-7-x86_64-NetInstall-2009.iso

real    0m0,875s
user    0m0,826s
sys     0m0,048s
cbfbe92e824020beeba880aa9d3aac816ee9353f9996dd41c02d62ff3684c2f7f2f34e8097129560a7270f65cb350c33 CentOS-7-x86_64-NetInstall-2009.iso

real    0m0,872s
user    0m0,811s
sys     0m0,060s
bee6f754580b167e13975b919605d704be726b8da53d0976a5266d2d3eac5c07ddf3130a39a2d97548e8ba61e2e61636cfe11be03b8999fca0a47d4ddc6e8148 CentOS-7-x86_64-NetInstall-2009.iso

Bon l’exemple ci-dessous me semble donner un résultat totalement inversé. Il y a peut-être une explication mais là je sèche. Si t’as une idée, hésite pas à m’expliquer.

Toujours est-il, l’intérêt des sommes de contrôle est de les comparer avec celle publiée par l’émetteur du fichier. Par conséquent, il vaut mieux trouver cette valeur ailleurs qu’a l’endroit exact où tu télécharge un fichier : un attaquant qui aurait compromis un fichier pourrait avoir modifié la valeur de cette somme. J’ai regardé pour Debian et la somme est stockée au même endroit que l’ISO. Mais ils signent les fichiers de somme par clé GPG.

Bon j’ai téléchargé l’ISO DVD de Debian 12 debian-12.8.0-amd64-DVD-1.iso, le fichier de somme de contrôle SHA256SUMS et le fichier de signature de celui-ci SHA256SUMS.sign.
On va d’abord chercher à déterminer par quelle clé le fichier de somme de contrôle a été signé.

Bash
antoine@dressing:~$ gpg --verify SHA256SUMS.sign SHA256SUMS 
gpg: Signature faite le sam. 09 nov. 2024 06:35:04 -10
gpg:                avec la clef RSA DF9B9C49EAA9298432589D76DA87E80D6294BE9B
gpg: Impossible de vérifier la signature : No public key

On télécharge la clé gpg correspondante.

Bash
antoine@dressing:~$ gpg --keyserver keyring.debian.org --recv-keys 0xDF9B9C49EAA9298432589D76DA87E80D6294BE9B
gpg: clef DA87E80D6294BE9B : clef publique « Debian CD signing key <debian-cd@lists.debian.org> » importée
gpg:       Quantité totale traitée : 1
gpg:                     importées : 1

Ouf, on peut enfin vérifier que le fichier… de vérification est Ok.

Bash
antoine@dressing:~$ gpg --verify SHA256SUMS.sign SHA256SUMS 
gpg: Signature faite le sam. 09 nov. 2024 06:35:04 -10
gpg:                avec la clef RSA DF9B9C49EAA9298432589D76DA87E80D6294BE9B
gpg: Bonne signature de « Debian CD signing key <debian-cd@lists.debian.org> » [inconnu]
gpg: Attention : cette clef n'est pas certifiée avec une signature de confiance.
gpg:             Rien n'indique que la signature appartient à son propriétaire.
Empreinte de clef principale : DF9B 9C49 EAA9 2984 3258  9D76 DA87 E80D 6294 BE9B

Bon techniquement, SHA256SUMS a bien été signé par une clé gpg de Debian mais mon système ne sait pas s’il doit faire confiance à cette clé. On va le rassurer et lui indiquer le niveau de confiance que l’on applique à cette clé.

Bash
antoine@dressing:~$ gpg --edit-key DF9B9C49EAA9298432589D76DA87E80D6294BE9B
gpg (GnuPG) 2.2.40; Copyright (C) 2022 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.


pub  rsa4096/DA87E80D6294BE9B
     créé : 2011-01-05  expire : jamais      utilisation : SC  
     confiance : inconnu       validité : inconnu
sub  rsa4096/642A5AC311CD9819
     créé : 2011-01-05  expire : jamais      utilisation : E   
[ inconnue] (1). Debian CD signing key <debian-cd@lists.debian.org>

gpg> trust
pub  rsa4096/DA87E80D6294BE9B
     créé : 2011-01-05  expire : jamais      utilisation : SC  
     confiance : inconnu       validité : inconnu
sub  rsa4096/642A5AC311CD9819
     créé : 2011-01-05  expire : jamais      utilisation : E   
[ inconnue] (1). Debian CD signing key <debian-cd@lists.debian.org>

Décidez maintenant de la confiance que vous portez en cet utilisateur pour
vérifier les clefs des autres utilisateurs (en regardant les passeports, en
vérifiant les empreintes depuis diverses sources, etc.)

  1 = je ne sais pas ou n'ai pas d'avis
  2 = je ne fais PAS confiance
  3 = je fais très légèrement confiance
  4 = je fais entièrement confiance
  5 = j'attribue une confiance ultime
  m = retour au menu principal

Quelle est votre décision ? 4

pub  rsa4096/DA87E80D6294BE9B
     créé : 2011-01-05  expire : jamais      utilisation : SC  
     confiance : totale        validité : inconnu
sub  rsa4096/642A5AC311CD9819
     créé : 2011-01-05  expire : jamais      utilisation : E   
[ inconnue] (1). Debian CD signing key <debian-cd@lists.debian.org>
Veuillez remarquer que la validité affichée pour la clef n'est pas
forcément correcte avant d'avoir relancé le programme.

Bon je l’ai lancé deux fois, la seconde fois je n’ai pas reçu l’avertissement. Mais lorsque je vérifie la signature de SHA256SUMS, gpg n’est quand même pas totalement jouasse.

Bash
antoine@dressing:~$ gpg --verify SHA256SUMS.sign SHA256SUMS 
gpg: Signature faite le sam. 09 nov. 2024 06:35:04 -10
gpg:                avec la clef RSA DF9B9C49EAA9298432589D76DA87E80D6294BE9B
gpg: Bonne signature de « Debian CD signing key <debian-cd@lists.debian.org> » [inconnu]
gpg: Attention : cette clef n'est pas certifiée avec une signature de confiance.
gpg:             Rien n'indique que la signature appartient à son propriétaire.
Empreinte de clef principale : DF9B 9C49 EAA9 2984 3258  9D76 DA87 E80D 6294 BE9B

Malgré ce résultat en demie teinte, je vais quand même vérifier que mon ISO n’est pas altéré (l’empreinte de la clé correspond à celle publiée sur le site de Debian).

Bash
antoine@dressing:~$ sha256sum --check SHA256SUMS
debian-12.8.0-amd64-DVD-1.iso: Réussi
sha256sum: debian-12.8.0-amd64-DVD-10.iso: Aucun fichier ou dossier de ce type
debian-12.8.0-amd64-DVD-10.iso: Échec d'ouverture ou de lecture
sha256sum: debian-12.8.0-amd64-DVD-11.iso: Aucun fichier ou dossier de ce type
debian-12.8.0-amd64-DVD-11.iso: Échec d'ouverture ou de lecture
sha256sum: debian-12.8.0-amd64-DVD-12.iso: Aucun fichier ou dossier de ce type
debian-12.8.0-amd64-DVD-12.iso: Échec d'ouverture ou de lecture
sha256sum: debian-12.8.0-amd64-DVD-13.iso: Aucun fichier ou dossier de ce type
debian-12.8.0-amd64-DVD-13.iso: Échec d'ouverture ou de lecture
sha256sum: debian-12.8.0-amd64-DVD-14.iso: Aucun fichier ou dossier de ce type
debian-12.8.0-amd64-DVD-14.iso: Échec d'ouverture ou de lecture
sha256sum: debian-12.8.0-amd64-DVD-15.iso: Aucun fichier ou dossier de ce type
debian-12.8.0-amd64-DVD-15.iso: Échec d'ouverture ou de lecture
sha256sum: debian-12.8.0-amd64-DVD-16.iso: Aucun fichier ou dossier de ce type
debian-12.8.0-amd64-DVD-16.iso: Échec d'ouverture ou de lecture
sha256sum: debian-12.8.0-amd64-DVD-17.iso: Aucun fichier ou dossier de ce type
debian-12.8.0-amd64-DVD-17.iso: Échec d'ouverture ou de lecture
sha256sum: debian-12.8.0-amd64-DVD-18.iso: Aucun fichier ou dossier de ce type
debian-12.8.0-amd64-DVD-18.iso: Échec d'ouverture ou de lecture
sha256sum: debian-12.8.0-amd64-DVD-19.iso: Aucun fichier ou dossier de ce type
debian-12.8.0-amd64-DVD-19.iso: Échec d'ouverture ou de lecture
sha256sum: debian-12.8.0-amd64-DVD-2.iso: Aucun fichier ou dossier de ce type
debian-12.8.0-amd64-DVD-2.iso: Échec d'ouverture ou de lecture
sha256sum: debian-12.8.0-amd64-DVD-20.iso: Aucun fichier ou dossier de ce type
debian-12.8.0-amd64-DVD-20.iso: Échec d'ouverture ou de lecture
sha256sum: debian-12.8.0-amd64-DVD-21.iso: Aucun fichier ou dossier de ce type
debian-12.8.0-amd64-DVD-21.iso: Échec d'ouverture ou de lecture
sha256sum: debian-12.8.0-amd64-DVD-3.iso: Aucun fichier ou dossier de ce type
debian-12.8.0-amd64-DVD-3.iso: Échec d'ouverture ou de lecture
sha256sum: debian-12.8.0-amd64-DVD-4.iso: Aucun fichier ou dossier de ce type
debian-12.8.0-amd64-DVD-4.iso: Échec d'ouverture ou de lecture
sha256sum: debian-12.8.0-amd64-DVD-5.iso: Aucun fichier ou dossier de ce type
debian-12.8.0-amd64-DVD-5.iso: Échec d'ouverture ou de lecture
sha256sum: debian-12.8.0-amd64-DVD-6.iso: Aucun fichier ou dossier de ce type
debian-12.8.0-amd64-DVD-6.iso: Échec d'ouverture ou de lecture
sha256sum: debian-12.8.0-amd64-DVD-7.iso: Aucun fichier ou dossier de ce type
debian-12.8.0-amd64-DVD-7.iso: Échec d'ouverture ou de lecture
sha256sum: debian-12.8.0-amd64-DVD-8.iso: Aucun fichier ou dossier de ce type
debian-12.8.0-amd64-DVD-8.iso: Échec d'ouverture ou de lecture
sha256sum: debian-12.8.0-amd64-DVD-9.iso: Aucun fichier ou dossier de ce type
debian-12.8.0-amd64-DVD-9.iso: Échec d'ouverture ou de lecture
sha256sum: Attention : 20 fichiers de la liste n'ont pas pu être lus
Voir plus

J’aurai dû m’y attendre : j’ai pas téléchargé non plus tous les fichiers, juste le DVD 1. On va alors ignorer les fichiers absents.

Bash
antoine@dressing:~$ sha256sum --check SHA256SUMS --ignore-missing 
debian-12.8.0-amd64-DVD-1.iso: Réussi

Bon te parler de nano et de vim ça m’a pas passionné plus que ça mais il y a quelques points dont j’ai tiré bénéfice très rapidement :

  • Virer les erreurs de du vers /dev/null c’est vachement chouette (ça évite le --exclude=),
  • Utiliser une regexp (genre la plus basique) pour extraire les lignes d’un log Apache qui commencent par les deux premiers octets d’un réseau /16.
  • Vérifier que les checksums d’un fichier soient identiques après l’avoir déplacé vers un autre serveur et être certain que je puisse le supprimer du premier.

Je garde quand même une saveur aigre-douce pour les temps de calculs qui semblent inversés par rapport à ce à quoi je m’attendais.

Dans tous les cas, le fait de (re)voir les bases reste un travail très intéressant.

Ah oui, si tu as raté le commentaire de Tzkuat en fin d’article précédent : les supports de cours LPIC-1 sont disponibles gratuitement en français et il y a même une version pdf.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *