En cours de rédaction

Utiliser diff et patch sous Linux

Cet article a été mis à jour, vous consultez ici une archive de cet article!
Table des matières

diff-patch



Introduction



Les commandes diff et patch sont des outils utilisés dans les systèmes Linux pour comparer et appliquer des différences entre des fichiers.

La commande diff est utilisée pour comparer le contenu de deux fichiers et afficher les différences ligne par ligne.
On pourra stocker ces différences dans un fichier via une redirection (par exemple maj.patch)
On pourra ensuite appliquer la mise à jour d'une copie de ce même fichier source grâce à la commande patch.

Cela permet :
- d'éviter de retransférer le fichier complet
- de patcher plusieurs fichiers dans plusieurs dossiers
- d'appliquer une modification rapidement
- de pouvoir annuler la modification faite rapidement

On retrouvera le type d'affichage produit par la commande diff dans des outils de gestion de version tels que git.

Ces 2 commandes sont utiles pour des fichiers texte.
Si les fichiers sont binaires, diff ne va pas analyser le contenu mais des métadonnées sur le fichier (taille, entête, etc...)



La commande diff



La commande diff est essentiellement utilisée pour comparer le contenu de deux fichiers et afficher les différences ligne par ligne.

Si la commande n'est pas disponible, on installera le paquet diffutils.

Voici la syntaxe :

Code BASH :
diff [options] fichier1 fichier2


Afin de voir cela avec un exemple, on va créer 2 fichiers :

livre.txt :
Code TEXT :
Il était un
petiot navire
Il était un
petit navire
qui n'avait ja-ja jamais navigué


livre-correction.txt en corrigeant la faute ligne 2 :
Code TEXT :
Il était un
petit navire
Il était un
petit navire
qui n'avait ja-ja jamais navigué


Sans options, la commande diff suivante :
Code BASH :
diff livre.txt livre-correction.txt


Va renvoyer :
Code TEXT :
2c2
< petiot navire
---
> petit navire


La syntaxe signifie :
  • 2c2 : indique que la différence se trouve à la ligne 2 dans le premier fichier et à la ligne 2 dans le deuxième fichier.
  • < petiot navire : ligne présente uniquement dans le premier fichier et contient le texte "petiot navire".
  • --- ligne de séparation des deux blocs de différences.
  • > petit navire : ligne présente uniquement dans le deuxième fichier et contient le texte "petit navire".


Il existe des options utiles :
  • -u : Affiche au format unifié
  • -r : Compare 2 répertoires de façon récursive
  • -q : Indique si les fichiers sont différents (sans afficher les différences)
  • -s : Indique clairement que les fichiers sont identiques (sinon, aucun résultat n'est produit)


À titre personnel, qu'on ait des fichiers, ou des dossiers, j'utilise quasiment systémariquement les options "-rus".
L'option -r n'a aucun effet si ce sont des fichiers.


Voici une sortie plus familière avec l'option -u :
Code BASH :
diff -u livre.txt livre-correction.txt


La sortie générée est
Code TEXT :
--- livre.txt   2024-02-13 18:04:50.213456100 +0100
+++ livre-correction.txt        2024-02-13 18:05:03.053789097 +0100
@@ -1,5 +1,5 @@
 Il était un
-petiot navire
+petit navire
 Il était un
 petit navire
 qui n'avait ja-ja jamais navigué
 


Si vous n'êtes pas familier avec cette sortie, voici la signification :
  • --- livre.txt : Nom du premier fichier
  • +++ livre-correction.txt : Nom du deuxième fichier
  • @@ -1,5 +1,5 @@ : Partie du fichier où les changements ont été apportés. Ici, les modifications ont commencé à la ligne 1 et se sont étendues sur 5 lignes dans chaque fichier.
  • -petiot navire : Ligne qui a été supprimée dans le premier fichier.
  • +petit navire : Ligne qui a été ajoutée dans le deuxième fichier.
  • Le reste du texte représente le contenu des fichiers à partir de la ligne où les changements ont été détectés.


Evidemment, on pourra générer un fichier de patch ainsi avec une simple redirection :
Code BASH :
diff -u livre.txt livre-correction.txt > maj.patch


Si on a plusieurs fichiers à corriger, on pourra ajouter à la suite les corrections sans soucis.
Afin que patch sache quels fichiers traités, l'option -u est obligatoire à chaque invocation de diff, afin d'inclure le nom des fichiers à patcher :
Code BASH :
diff -u autre_fichier.txt autre_fichier-correction.txt >> maj.patch



Si vous faites une sauvegarde de fichiers avec un rsync par exemple sur un disque externe, vous pouvez avec diff vérifier que les 2 dossiers ont tous les fichiers et qu'ils sont strictement identiques si la commande suivante ne vous retourne rien :
Code BASH :
diff -rq source/ destination/



La commande patch



La commande patch, quant à elle, est utilisée pour appliquer des différences générées par la commande diff à un fichier source. Elle permet d'appliquer des correctifs ou des modifications à un fichier de manière automatique.

Si la commande n'est pas disponible, on installera le paquet patch.

Voici la syntaxe :

Code BASH :
patch [options] fichier_a_patcher < fichier.patch


Il existe des options utiles :
  • -b : Garde une copie du fichier source avec l'extension .orig
  • -R : Inverse l'effet de patch (pratique pour "annuler un patch")


Si nous reprenons notre exemple, on imagine que notre destinataire n'a que le fichier (à patcher) livre.txt. On va simplement lui envoyer le fichier maj.patch

On appliquera la modification comme ceci :
Code BASH :
patch livre.txt < maj.patch 


Le système indique que l'opération s'est bien déroulée :
Code TEXT :
patching file livre.txt


Pour annuler un patch, avec l'option -R on utilisera la commande comme ceci :
Code BASH :
patch -R livre.txt < correction.patch


Si le diff a été fait avec l'option -u (format unifié), le nom du fichier à patcher est facultatif si c'est le même que celui indiqué dans le patch (premier fichier).
On pourra donc utiliser :
Code BASH :
patch < maj.patch



Dans le cas où plusieurs fichiers ont été patchés, on utilisera la syntaxe sans spécifier le nom des fichiers à patcher et on aura le retour suivant :
Code TEXT :
patching file livre.txt
patching file livre2.txt



Commande patch avec une arborescence différente



Il existe une option intéressante dans le cas où l'arborescence diffère, c'est l'option -pN où :
  • -p : Option à patch qui indique de supprimer les premiers N composants des noms de répertoire mentionnés dans le diff avant d'appliquer les modifications.
  • N : entier qui spécifie le nombre de composants de chemin à supprimer.


Je prends un exemple où on a un serveur de test où l'arborescence est :
Code :
test/
  |- livre.txt
  |- livre2.txt


Et le serveur de prod est :
Code :
prod/
  |- livre.txt
  |- livre2.txt


Si le patch a été fait dans le dossier parent de "test", on a les chemins de fichiers sous cette forme :
Code TEXT :
--- test/livre2.txt    2024-02-13 19:20:42.289398937 +0100
+++ test/livre2-correction.txt    2024-02-13 19:22:43.760479078 +0100


Or, cette arborescence sur le serveur de production n'existe pas, elle est différente.

On ne pourra pas patcher car les fichiers ne se trouvent pas dans le dossier "test".
On placera dans ce cas le patch dans le dossier "prod" directement, et on demandera à patch de supprimer un niveau de répertoire :
Code BASH :
patch -p1 < correction.patch