Ce billet se propose, sur la base d'une curiosité, d’explorer quelques fonctionnalités autour bash et des commandes apt, diff, sed et sort.
Présentation
Dans un précédent billet 📦 Lister et enregistrer les paquets installés sur votre Linux, j’affirmais que :
dpkg --get-selections
apt list --installed
… donnaient la même information.
Eh bien, il est possible de le vérifier avec la commande suivante :
diff --side-by-side --suppress-common-lines <( dpkg 2>/dev/null --get-selections | sed -E 's,^([^[:blank:]:]+).*,\1,g' | sort ) <( apt 2>/dev/null list --installed | sed -E 's,^([^/]*)/.*,\1,g' | sort )
Mais c’est pas forcément très clair, n’est-ce pas ?
On peut reformater comme cela :
diff --side-by-side --suppress-common-lines <(
dpkg 2>/dev/null --get-selections |
sed -E 's,^([^[:blank:]:]+).*,\1,g' |
sort
) <(
apt 2>/dev/null list --installed |
sed -E 's,^([^/]*)/.*,\1,g' |
sort
)
L’affichage attendu est « presque » vide (ce qui est attendu du fait du drapeau --suppress-common-lines
), je propose de regarder le résultat sur votre Linux la ligne suivante :
diff --side-by-side <( dpkg 2>/dev/null --get-selections | sed -E 's,^([^[:blank:]:]+).*,\1,g' | sort ) <( apt 2>/dev/null list --installed | sed -E 's,^([^/]*)/.*,\1,g' | sort )
Décortiquons
La ligne initiale peut se décomposer ainsi :
- La commande diff :
diff --side-by-side --suppress-common-lines XXX0 YYY0
Enfin, plus précisément ainsi, mais nous verrons plus en détail ce que cela veut dire un peu plus loin :
diff --side-by-side --suppress-common-lines <( XXX1 ) <( YYY1 )
Avec les deux commandes suivantes :
- La commande dpkg et le reformatage de sa sortie :
dpkg 2>/dev/null --get-selections | sed -E 's,^([^[:blank:]:]+).*,\1,g' | sort
- La commande apt et le reformatage de sa sortie :
apt 2>/dev/null list --installed | sed -E 's,^([^/]*)/.*,\1,g' | sort
La commande « diff »
La commande diff permet de comparer les fichiers ligne à ligne.
La syntaxe de base est :
diff FICHIER1.txt FICHIER2.txt
- Le drappeau
-y
ou--side-by-side
affiche le résultat de la commande sur deux colonnes. - Le drappeau
--suppress-common-lines
demande à ne pas afficher les lignes communes.
On note que la commande diff attend deux fichiers textes dans sa ligne de commande. Laissons ce problème de côté pour le momment.
Ceci correspond à cette version de la ligne de commande :
diff --side-by-side --suppress-common-lines XXX0 YYY0
La commande « apt » et le reformatage de sa sortie :
La commande apt est un système de gestion de paquets logiciels. Pour la gestion au quotidien des paquets.
apt 2>/dev/null list --installed | sed -E 's,^([^/]*)/.*,\1,g' | sort
Que l’on va décomposer ainsi :
apt list --installed
— pour commencerapt 2>/dev/null list --installed
— ensuite
puis :
sed -E 's,^([^/]*)/.*,\1,g'
sort
-
Le selecteur
list
est quelque peu similaire àdpkg-query --list
dans le sens où il peut afficher une liste de paquets répondant à certains critères. Il prend en charge l'options permettant de lister les versions installées (--installed
).
Ce qui correspond précisément à notre cas d'usage.
Cependant, la documentation de la commande apt
alerte sur son utilisation dans le script (ce qui nous le verrons puis loin est le cas ici).
La ligne de commande apt(8) est conçue comme un outil pour l'utilisateur final et son comportement peut changer entre les versions. Bien qu'elle essaie de ne pas rompre la compatibilité ascendante, cela n'est pas non plus garanti si un changement semble bénéfique pour une utilisation interactive.
Le pipeline |
qui suit la commande et permet de prendre le résultat de la commande apt pour retraiter par la suite fait que de fait, la commande apt est utilisée dans script. Cela est détecté par la commande apt qui affiche une alerte sur la sortie d'erreur (stderr). Pour éviter cet affichage on utilise la redirection de la sortie d'erreur (2>
) vers un fichier qui absorbe tout ce qu'on lui envoie : /dev/null
.
La commande complète apt 2>/dev/null list --installed
n’affichera donc pas les erreurs, ni le message d’alerte.
Le résultat sera envoyer vers la commande sed :
-
sed -E 's,^([^/]*)/.*,\1,g'
permet de n’afficher que ce qui est devant le caractère/
, le reste de chaque ligne sera ignoré. La commande sed est un éditeur de ligne, le traitement se fait donc ligne par ligne. -
sort
permet de trier les lignes ainsi générées.
La commande « dpkg » et le reformatage de sa sortie :
La commande dpkg est le gestionnaire de paquets pour Debian.
Le drapeau --get-selections
permet d’obtenir la liste des sélections des paquets, et l’envoie sur la sortie standard.
dpkg 2>/dev/null --get-selections | sed -E 's,^([^[:blank:]:]+).*,\1,g' | sort
On applique les mêmes principes que pour apt, sauf que l’expression régulière est différente.
On verra en annexe, ce le détail des deux expressions réguliers utilisées.
Remettons tous cela ensemble grâce à <( … )
Basiquement, on pourrait faire comme suit :
dpkg 2>/dev/null --get-selections | sed -E 's,^([^[:blank:]:]+).*,\1,g' | sort >XXX0
apt 2>/dev/null list --installed | sed -E 's,^([^/]*)/.*,\1,g' | sort >YYY0
diff --side-by-side --suppress-common-lines XXX0 YYY0
Les deux premières lignes effectuant les traitements expliqués et mettant chacun des résultats dans un fichier. Il suffit alors d’utiliser diff en précisant le nom des fichiers créés préalablement.
Cependant, cette solution implique de gérer des fichiers temporaires: s'assurer de ne pas écraser un fichier existant et supprimer les fichiers en fin de traitement.
C’est là que la notation <( … )
intervient, puisque c’est exactement ce qu’elle fait. Il n’y a alors plus besoin d'utiliser 3 étapes, on peut tout faire dans une même suite de commandes.
Liens
- Manuel de apt[FR], apt[EN]
- Manuel de diff[FR], diff[EN]
- Manuel de dpkg[FR], dpkg[EN]
- Manuel de sed[FR], sed[EN]
- Manuel de sort[FR], sort[EN]
- Liste des commandes abordées dans ce blog
ᦿ