Cours Linux / Comparer des fichiers : différences entre diff et cmp
Pour lire quelles lignes ont changé entre deux textes, utilisez diff ; pour comparer le contenu de deux fichiers octet par octet, utilisez cmp. Avec GNU Diffutils, l’état de sortie 0 signifie qu’aucune différence n’a été trouvée, 1 qu’une différence a été trouvée et 2 qu’un problème a empêché une comparaison normale. Une différence signalée n’est donc pas la même chose qu’une erreur d’accès.
Cette leçon utilise Bash et GNU Diffutils dans le Linux de pratique. Vous préparerez deux petits fichiers connus dont seule la deuxième ligne diffère. Le cours sur la lecture des fichiers avec cat, less, head et tail explique comment consulter leur contenu avant de les comparer.
Préparer deux fichiers dans un dossier neuf
linux-course-13-practice doit être un nouveau dossier de votre répertoire personnel. Si ce nom existe déjà, changez la valeur de candidat ; ne poursuivez pas dans un ancien répertoire. La variable comparaison n’est renseignée que lorsque mkdir et les deux écritures ont réussi :
comparaison=
candidat="$HOME/linux-course-13-practice"
if mkdir -- "$candidat" &&
printf 'alpha\nbeta\ngamma\n' > "$candidat/old.txt" &&
printf 'alpha\nBETA\ngamma\n' > "$candidat/new.txt"; then
comparaison=$candidat
printf 'Atelier : %s\n' "$comparaison"
else
printf '%s\n' 'Préparation échouée : vérifier et choisir un dossier neuf avant de poursuivre.' >&2
fi
old.txt contient alpha, beta, gamma, chacun suivi d’une fin de ligne. new.txt contient les mêmes première et troisième lignes, mais BETA en majuscules à la deuxième. La redirection > peut remplacer un contenu : elle est réservée ici aux fichiers de ce dossier neuf.
Si la préparation échoue, examinez le message et les éventuels fichiers partiellement créés avant de recommencer avec un autre nom. Gardez le même terminal ; les blocs suivants vérifient que comparaison n’est pas vide et utilisent des chemins complets entre guillemets. Ne redéfinissez pas cette variable vers d’autres données.
test -n "$comparaison" &&
cat -- "$comparaison/old.txt" &&
cat -- "$comparaison/new.txt"
Vous devez voir les trois lignes du premier fichier, puis les trois du second, avec beta puis BETA à la position attendue. Les commandes de comparaison qui suivent consultent ces fichiers sans réécrire leur contenu.
Lire les changements de lignes avec diff -u
diff compare les lignes et explique ce qui diffère. L’option -u demande le format unifié : les lignes modifiées sont accompagnées de contexte commun. Les deux --label donnent ici des en-têtes stables, sans horodatages variables :
test -n "$comparaison" &&
diff -u --label old --label new -- "$comparaison/old.txt" "$comparaison/new.txt"
La sortie attendue est exactement :
--- old
+++ new
@@ -1,3 +1,3 @@
alpha
-beta
+BETA
gamma
- --- old et +++ new identifient respectivement le premier et le second fichier de la commande. Ces noms sont nos étiquettes.
- @@ -1,3 +1,3 @@ indique un bloc couvrant trois lignes à partir de la ligne 1 dans chaque fichier.
- -beta est la ligne du premier fichier qui disparaît dans cette comparaison.
- +BETA est la ligne du second qui la remplace.
- Les lignes précédées d’un espace, alpha et gamma, sont le contexte inchangé.
La comparaison décrit un sens : du premier argument vers le second. diff ne sait pas lequel est historiquement l’original parce que vous l’avez nommé old.txt. Si vous inversez seulement les deux chemins et adaptez leurs étiquettes, BETA devient la ligne retirée et beta la ligne ajoutée. Le manuel GNU du format unifié explique les en-têtes, les plages et ces marqueurs.
Repérer la première différence d’octet avec cmp
cmp compare les octets. Sans option particulière, il signale la première différence rencontrée ; si un fichier n’est que le début de l’autre, il peut plutôt signaler la fin du fichier le plus court.
test -n "$comparaison" && cmp -- "$comparaison/old.txt" "$comparaison/new.txt"
Dans une locale anglaise, le message peut contenir differ: byte 7, line 2 ou differ: char 7, line 2, précédé des noms de fichiers. Dans cette indication de cmp, char désigne également une position d’octet, pas un caractère Unicode décodé. Les chemins, la version et la langue du message peuvent faire varier la présentation. Dans notre texte ASCII avec fins de ligne LF, alpha occupe les octets 1 à 5, le saut de ligne est l’octet 6, puis b ou B occupe l’octet 7. Les positions de cmp commencent à 1 ; un octet n’est pas nécessairement un caractère entier dans d’autres encodages.
Cette sortie ne présente pas toutes les lignes changées comme diff -u. Le mode par défaut s’arrête au premier écart ; GNU cmp propose aussi -l pour lister les positions et valeurs des octets différents. Pour demander seulement un verdict, -s supprime la sortie : il faut alors examiner l’état de sortie. Le manuel GNU cmp détaille le message et les options.
Lire l’état de sortie sans le confondre avec un message
Dans Bash, $? contient l’état de la commande exécutée juste avant. Capturez-le immédiatement, avant une autre consultation, un echo ou une nouvelle comparaison :
if test -n "$comparaison"; then
cmp -s -- "$comparaison/old.txt" "$comparaison/new.txt"
etat=$?
printf 'Etat cmp : %s\n' "$etat"
fi
Dans cette session interactive Bash ordinaire, vous devez lire Etat cmp : 1. L’affichage de printf est une nouvelle commande ; relire $? après ce printf ne mesurerait plus le cmp précédent. Les deux fichiers sont accessibles, mais leurs contenus diffèrent.
| État GNU diff / cmp | Sens | Ce qu’il faut vérifier |
|---|---|---|
| 0 | Aucune différence selon la comparaison demandée | Les deux cibles et les options sont bien celles voulues |
| 1 | Une différence a été trouvée | Le changement attendu, avec diff si c’est du texte |
| 2 | Problème de comparaison | Les chemins, les droits et les diagnostics |
Pour observer 0 sans modifier les données, comparez old.txt à lui-même en remplaçant le second chemin du bloc par "$comparaison/old.txt". Le mode silencieux ne doit rien afficher avant Etat cmp : 0.
Pour distinguer une erreur d’un simple écart, notre dossier neuf ne contient aucun fichier absent.txt. Essayez ce chemin volontairement manquant, sans le créer :
if test -n "$comparaison"; then
cmp -- "$comparaison/old.txt" "$comparaison/absent.txt"
etat=$?
printf 'Etat cmp : %s\n' "$etat"
fi
Un diagnostic d’accès au fichier absent doit apparaître et Etat cmp : 2 doit suivre. Ne traitez pas cet état comme un troisième type de changement de contenu. Ces valeurs sont documentées pour GNU diff et GNU cmp ; d’autres commandes donnent un autre sens aux mêmes nombres.
Si vous intégrez plus tard ces comparaisons dans un script qui utilise set -e, une commande renvoyant 1 peut interrompre le script avant la capture illustrée ici. Traitez alors le résultat dans une condition explicite : ne recopiez pas sans adaptation une séquence interactive dans une automatisation.
Changer une seule entrée, puis revenir à la différence initiale
Pour vérifier que les outils constatent bien le contenu, changez seulement la deuxième ligne de new.txt en beta. Cette écriture est limitée à notre fichier de démonstration :
if test -n "$comparaison"; then
printf 'alpha\nbeta\ngamma\n' > "$comparaison/new.txt" && {
cmp -s -- "$comparaison/old.txt" "$comparaison/new.txt"
etat=$?
printf 'Etat cmp : %s\n' "$etat"
}
fi
Vous devez maintenant lire 0. Relancez aussi la commande diff -u du début : elle ne produit plus de différence et renvoie 0. Les noms et les options sont restés les mêmes ; seul le contenu préparé dans new.txt a changé.
Rétablissez ensuite les majuscules de notre état initial :
if test -n "$comparaison"; then
printf 'alpha\nBETA\ngamma\n' > "$comparaison/new.txt" && {
cmp -s -- "$comparaison/old.txt" "$comparaison/new.txt"
etat=$?
printf 'Etat cmp : %s\n' "$etat"
}
fi
Vous retrouvez 1. Réexécutez diff -u : le bloc doit de nouveau afficher -beta puis +BETA. old.txt n’a pas été modifié, et les deux fichiers peuvent rester dans le dossier pour refaire la comparaison.
Choisir le critère de comparaison adapté
| Besoin | Choix | Limite |
|---|---|---|
| Comprendre des lignes de texte modifiées | diff -u | Le sens dépend de l’ordre des fichiers et des options |
| Comparer tout le contenu octet par octet | cmp sans option qui limite la comparaison | La sortie par défaut indique seulement le premier écart |
| Obtenir uniquement un état | cmp -s | Il faut distinguer 0, 1 et 2 |
| Comparer des arborescences | diff -r, après vérification des chemins | Le périmètre dépasse les deux fichiers de cet atelier |
Sur Linux, des fins de ligne ou des encodages différents peuvent produire des fichiers différents alors que le texte paraît semblable à l’écran. Un fichier binaire peut aussi ne pas fournir une explication ligne par ligne utile avec diff ; cmp convient à une comparaison des octets. N’activez pas une option qui ignore une différence si votre objectif est une identité complète.
Par exemple, diff -i ignore les différences de casse reconnues par sa comparaison : dans notre cas ASCII, beta et BETA ne seraient plus un écart, alors que cmp continuerait à trouver des octets différents. Un état 0 décrit donc toujours la comparaison effectivement demandée. En cas de doute, contrôlez les chemins, relisez les deux entrées et retirez les options d’exclusion avant d’interpréter le verdict.




