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
- il video su YouTube
- esempi e comandi della puntata su GitHub
- Manuale di GNU diffutils — il manuale completo
- Invocare diff — tutte le opzioni
- Invocare patch — per applicare le differenze
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!
