GNU/Linux: trovare le differenze con diff

Oggi un comando molto utile a sistemisti e sviluppatori: diff, che calcola le differenze fra due file. Ci dice in cosa differiscono e, volendo, produce uno script che li rende uguali. Funziona con qualunque file di testo: codice sorgente, ma anche documentazione e configurazioni. Qui usiamo file semplicissimi, ma quello che impari vale anche per file lunghi e complicati.

Opzioni comuni

  • -b ignora gli spazi e i tab multipli;
  • -w ignora completamente spazi e tab;
  • -B ignora le linee vuote;
  • -i ignora le differenze fra maiuscole e minuscole;
  • -q produce output solo se ci sono differenze.

I due file di prova

Riempiamo file1 con una lista di frutta:

mele
pere
arance
ciliege
pesche

e file2 uguale, ma con le ultime due righe diverse:

mele
pere
arance
kiwi
albicocche

Output normale

Nella forma più semplice si danno i due file:

diff file1 file2

L’output:

4,5c4,5
< ciliege
< pesche
---
> kiwi
> albicocche

Si legge così. I primi due numeri sono l’intervallo di righe nel primo file, gli ultimi due l’intervallo nel secondo. La lettera in mezzo è il tipo di differenza:

  • a (add): righe da aggiungere;
  • c (change): righe da cambiare;
  • d (delete): righe da eliminare.

Le righe con < sono del primo file, quelle con > del secondo, e tre trattini fanno da separatore. Per rendere file1 uguale a file2 devo quindi cambiare ciliege e pesche in kiwi e albicocche. Al contrario se voglio adattare file2 a file1.

Differenze con contesto

Con -c ottengo un output più esteso ma più parlante: oltre alle righe diverse mostra quelle attorno, e in cima i nomi dei file con la data di modifica, utile per capire quale è stato toccato prima.

diff -c file1 file2

L’output, senza le prime due righe con i nomi e le date, è questo:

***************
*** 1,5 ****
  mele
  pere
  arance
! ciliege
! pesche
--- 1,5 ----
  mele
  pere
  arance
! kiwi
! albicocche

Gli asterischi indicano il file1, i trattini il file2, e la lunga riga di asterischi separa l’intestazione dal contesto. I prefissi:

  • ! riga cambiata;
  • + riga aggiunta;
  • – riga eliminata.

Contesto unificato

Con -u il contesto è unificato: più sintetico, senza informazioni ridondanti.

diff -u file1 file2

L’output (sempre senza le due righe di intestazione con le date):

@@ -1,5 +1,5 @@
 mele
 pere
 arance
-ciliege
-pesche
+kiwi
+albicocche

Qui il meno è il file1 e il più il file2. La riga con le doppie chiocciole dice che stiamo guardando le righe da 1 a 5 del primo file (con il meno) e da 1 a 5 del secondo (con il più). Le righe senza prefisso sono contesto uguale nei due file. All’inizio sembra ostico, ma è comodissimo per capire al volo le differenze.

Due colonne

Infine -y mette i file a confronto affiancati, con una pipe sulle righe diverse. Personalmente lo uso poco perché lo trovo caotico, ma c’è chi lo apprezza.

diff -y file1 file2

L’output:

mele           mele
pere           pere
arance         arance
ciliege      | kiwi
pesche       | albicocche

(le colonne reali sono più larghe: qui ho accorciato per stare nelle righe.)

Aggiungere una riga

Aggiungiamo la riga fragole in fondo a file2:

echo fragole >> file2
diff file1 file2

L’output normale diventa:

4,5c4,6
< ciliege
< pesche
---
> kiwi
> albicocche
> fragole

Con le altre modalità si capisce altrettanto facilmente: nel formato unificato compare una riga con il più davanti.

Eliminare una riga

Riportiamo i due file uguali e togliamo la riga pere dal secondo. Qui lo faccio con sed:

cp file1 f1
cp file1 f2
sed -i '/pere/d' f2
diff f1 f2

L’output:

2d1
< pere

Si legge: la riga 2 del primo file va eliminata (d) per corrispondere al secondo file dalla riga 1. Leggere questo formato richiede un po’ di pratica, ma poi non ha segreti. Con il contesto (-c o -u) la riga pere compare con il meno davanti, e con -y vedi la riga senza la controparte, segnata con una freccia.

Patch e script

Con diff si possono produrre file da applicare con altri comandi. Il formato unificato si usa con patch:

diff -u file1 file2 > script.txt
patch < script.txt

Per annullare la patch:

patch -R < script.txt

C’è poi la modalità -e, che crea uno script per ed:

diff -e file1 file2

Per renderlo operativo lo salvo e aggiungo il comando di salvataggio w:

diff -e file1 file2 > script.txt
echo w >> script.txt

e lo applico con ed:

(cat script.txt && echo w) | ed - file1

L’argomento patch merita una puntata a sé.

In sintesi

  • diff file1 file2 mostra le differenze (formato normale);
  • -c dà il contesto, -u il contesto unificato, -y le due colonne;
  • a, c, d dicono se aggiungere, cambiare o eliminare;
  • -u con patch permette di applicare le modifiche, -R di annullarle;
  • l’unica cosa che conta è fare pratica con i vari formati.

Link e risorse

Sostieni il canale

PER SOSTENERE IL CANALE BASTANO UN LIKE E UN’ISCRIZIONE!

questi link sono affiliati Amazon, acquistando i libri che ti consiglio sostieni il canale!