Oggi vi parlo del comando patch, uno strumento molto utile per applicare rapidamente delle modifiche a un file. Storicamente serve per aggiornare i sorgenti del software prima della compilazione, e più avanti vedremo che si può usare anche dentro un makefile per applicare le patch prima di compilare. Ma ha utilizzi che vanno ben oltre: come abbiamo già visto per diff, funziona su qualunque file di testo, quindi anche documentazione, pagine HTML, CSS, script, file di configurazione.
Da diff dobbiamo partire per ottenere il file di patch che patch può applicare al file di destinazione.
Applicare una patch
Riprendiamo i due file di prova dell’ultima volta. Il primo ha una riga in più del secondo:
cat file1.txt
L’output è:
mela
pera
arancia
E il secondo:
cat file2.txt
L’output è:
mela
arancia
Se lanciamo diff con l’opzione -u otteniamo un diff in formato unificato, che già conosciamo:
diff -u file1.txt file2.txt
L’output è:
--- file1.txt
+++ file2.txt
@@ -1,3 +1,2 @@
mela
-pera
arancia
Le righe riportano anche data e ora dei file, che qui ho omesso. Questo formato è comodo da leggere per gli umani ed è leggibile anche da patch. Quindi invece di scriverlo a terminale lo scrivo su un file:
diff -u file1.txt file2.txt > patch.txt
Ora passiamo il file di patch a patch in input:
patch < patch.txt
Patch ci risponde che ha modificato il file:
patching file file1.txt
Come fa a sapere che deve modificare file1.txt? Perché c’è scritto nell’output unificato: file1.txt è il file rispetto al quale sono calcolate le differenze, file2.txt è quello aggiornato, il modello. Se facciamo il cat dei due file vediamo che file1.txt ha perso la riga pera, mentre file2.txt non è stato toccato:
cat file1.txt
L’output è:
mela
arancia
Tornare indietro: -R
Cosa succede se faccio le mie verifiche, provo la patch e non funziona? Qui sta la comodità estrema del comando, che a mio parere vale da sola l’intera utilità di patch: con una sola riga posso annullare tutte le modifiche e tornare allo stato precedente. Basta chiamare patch con -R (maiuscola, da reverse) e dargli in pasto lo stesso identico file di patch usato per applicare le modifiche:
patch -R < patch.txt
Anche ora patch lavora su file1.txt:
patching file file1.txt
Se rifaccio il cat di file1.txt vedo che la riga pera è riapparsa.
Questo l’abbiamo fatto con un file, ma si può fare su intere cartelle, ricorsivamente: posso applicare una serie di patch, verificarle rapidamente e poi tornare indietro altrettanto in fretta.
Il backup: -b
Prima di concludere su patch, un’opzione molto comoda: -b crea una copia di backup del file prima di modificarlo, cioè prima di applicare la patch:
patch -b < patch.txt
L’output di patch è sempre lo stesso:
patching file file1.txt
Ma se faccio un ls trovo un file nuovo:
ls
L’output è:
file1.txt file1.txt.orig file2.txt patch.txt
Il file file1.txt.orig contiene il contenuto originario di file1.txt, quindi con ancora la riga pera, mentre file1.txt è stato correttamente modificato.
Altre opzioni utili, dalla man page:
- –dry-run stampa il risultato dell’applicazione senza modificare nessun file;
- -d dir (o –directory=dir) entra nella cartella dir prima di fare qualsiasi altra cosa;
- -c / –context lavora con i diff in formato di contesto;
- -D define (o –ifdef=define) marca le modifiche con i costrutti #ifdef … #endif;
- –help stampa il riepilogo delle opzioni;
- -v / –version stampa la versione.
Un altro modo: gli script di ed
Se vi ricordate il video su diff, a un certo punto avevamo nominato un altro modo per applicare le modifiche automaticamente: uno script di ed. Ed è un editor particolarissimo. Se qualcuno di voi ritiene vi e pico spartani, deve vedere ed: risale addirittura agli anni 60, era un componente standard di Unix e ce lo siamo portati dietro fino a oggi. Perché? Perché è comodissimo per scriptare modifiche ai file di testo. Supponete di avere un grande file a cui volete applicare delle regole di formattazione: il modo più semplice è uno script di ed. Se volete approfondire, fatemelo sapere nei commenti: è un editor veramente esoterico, ma altrettanto potente.
Per non divagare ho preparato una cartella con i soliti file1.txt e file2.txt, stavolta con qualche modifica in più: c’è una riga in meno, una riga cambiata e un’altra riga in meno, così apprezziamo di più il lavoro di ed. Cominciamo con un normale diff unificato fra i due file, che ci dice di eliminare due righe (pera e banane), aggiungere la riga ciocco banane (non so cosa sia, ma voglio provarlo) e sottrarre la riga cocomeri. Queste modifiche vanno trasformate in uno script comprensibile a ed, quindi usiamo l’opzione -e al posto di -u:
diff -e file1.txt file2.txt
L’output è ancora abbastanza intellegibile, se ricordate come si legge l’output di diff. Con i nomi di frutta del mio esempio diventa:
6d
2,3c
ciocco banane
.
Tradotto in termini umani: cancella la riga 6 (cocomeri) e cambia le righe 2 e 3 con la stringa ciocco banane, cioè elimina le righe 2 e 3 e inserisci quella riga. Il punto su riga singola termina l’input. Chi ha usato smtp su telnet ricorderà che anche lì il punto su riga singola termina la mail.
Ora salviamo lo script su un file:
diff -e file1.txt file2.txt > patch.txt
Nel file c’è esattamente quello che ci aspettiamo. A questo punto dobbiamo dire a ed di scrivere, perché ed lavora su un buffer e solo alla fine lo scrive su file, con il comando w. Possiamo aggiungere w in fondo a patch.txt oppure passarlo in input a ed: cambia poco. Se mettiamo la scrittura direttamente nel file non possiamo fare prove a vuoto, ma il concetto è chiaro.
Il modo più semplice è passare lo script in pipe a ed, aggiungendo w con echo:
(cat patch.txt; echo w) | ed - file1.txt
Il primo argomento, il trattino, dice a ed di leggere i comandi dal pipe; poi indichiamo il file su cui lavorare. Ed è un editor spartano e parco di output, quindi non ci dice nulla; ma proprio perché non ci ha dato errore capiamo che ha eseguito. Se guardiamo file1.txt vediamo che ora corrisponde esattamente a file2.txt: le modifiche di patch.txt sono state applicate.
Vediamo l’altra modalità con un esempio. Modifichiamo di nuovo file1.txt, aggiungendo cose a caso, e ricalcoliamo le differenze: questa volta ci sono due righe da cancellare, pesche e ciliege. Per applicare lo script di ed lo ricreiamo aggiungendo w alla fine:
diff -e file1.txt file2.txt > patch.txt
echo w >> patch.txt
Ora patch.txt contiene alla fine il comando w, che sta per scrivi il buffer. Possiamo semplicemente fare:
cat patch.txt | ed - file1.txt
e otteniamo lo stesso risultato.
Conclusione
Una delle cose che amo di Linux è che ci sono mille modi di fare le cose, nessuno giusto o sbagliato a priori: abbiamo tanti strumenti, e sta nell’arte del sistemista capire quale è il migliore per quello specifico caso. In sintesi: diff -u produce una patch che patch applica con una riga, -R la annulla, -b salva una copia .orig del file, e per i più avventurosi diff -e produce uno script per ed. Come sempre, aprite la vostra Linux box, rompete qualcosa e capite come funzionano questi comandi: l’unico modo di imparare davvero è con la pratica.
Link e risorse
- il video su YouTube
- esempi e comandi della puntata su GitHub
- patch(1), pagina di manuale — il manuale completo
- diff(1), pagina di manuale — il comando che genera le patch
- Patch (informatica) — in italiano
Sostieni il canale
PER SOSTENERE IL CANALE BASTANO UN LIKE E UN’ISCRIZIONE!
ma se vuoi offrirmi un caffè, mi aiuterà a rimanere sveglio mentre registro i video 😁☕
clicca qui per scoprire come https://buymeacoffee.com/thelinuxnerd
questi link sono affiliati Amazon, acquistando i libri che ti consiglio sostieni il canale!
