it-swarm.it

Come diff file ignorando i commenti (righe che iniziano con #)?

Ho due file di configurazione, l'originale del gestore pacchetti e uno personalizzato modificato da me. Ho aggiunto alcuni commenti per descrivere il comportamento.

Come posso eseguire diff sui file di configurazione, saltando i commenti? Una riga commentata è definita da:

  • spazi bianchi iniziali facoltativi (schede e spazi)
  • cancelletto (#)
  • qualsiasi altro personaggio

L'espressione regolare (più semplice) che salta il primo requisito sarebbe #.*. Ho provato il --ignore-matching-lines=RE (-I RE) opzione di GNU diff 3.0, ma non sono riuscito a farlo funzionare con quel RE. Ho anche provato .*#.* e .*\#.* senza fortuna. Letteralmente mettendo la linea (Port 631) poiché RE non corrisponde a nulla, né aiuta a mettere la RE tra le barre.

Come suggerito in il sapore regex dello strumento "diff" sembra mancare? , ho provato grep -G:

grep -G '#.*' file

Questo sembra corrispondere ai commenti, ma non funziona per diff -I '#.*' file1 file2.

Quindi, come dovrebbe essere usata questa opzione? Come posso fare in modo che diff salti determinate righe (nel mio caso commenti)? Non suggerire grep di modificare il file e confrontare i file temporanei.

56
Lekensteyn

Secondo Gilles, l'opzione -I Ignora una riga solo se nient'altro all'interno di quel set corrisponde ad eccezione della corrispondenza di -I. Non l'ho capito fino a quando non l'ho provato.

Il test

Nel mio test sono coinvolti tre file:
File test1:

    text

File test2:

    text
    #comment

File test3:

    changed text
    #comment

I comandi:

$ # comparing files with comment-only changes
$ diff -u -I '#.*' test{1,2}
$ # comparing files with both comment and regular changes
$ diff -u -I '#.*' test{2,3}
--- test2       2011-07-20 16:38:59.717701430 +0200
+++ test3       2011-07-20 16:39:10.187701435 +0200
@@ -1,2 +1,2 @@
-text
+changed text
 #comment

Il modo alternativo

Dato che finora non ci sono risposte che spieghino come usare correttamente l'opzione -I, Fornirò un'alternativa che funziona con le shell bash:

diff -u -B <(grep -vE '^\s*(#|$)' test1)  <(grep -vE '^\s*(#|$)' test2)
  • diff -u - diff unificato
    • -B - ignora le righe vuote
  • <(command) - una funzione bash chiamata sostituzione del processo che apre un descrittore di file per il comando, questo elimina la necessità di un file temporaneo
  • grep - comando per la stampa di linee (non) corrispondenti a un motivo
    • -v: Mostra le righe non corrispondenti
    • E - usa espressioni regolari estese
    • '^\s*(#|$)' - un'espressione regolare che corrisponde a commenti e righe vuote
      • ^ - corrisponde all'inizio di una riga
      • \s*: Confronta gli spazi bianchi (schede e spazi) se presenti
      • (#|$) Corrisponde a un segno di hash o, in alternativa, alla fine di una riga
54
Lekensteyn

Provare:

diff -b -I '^#' -I '^ #' file1 file2

Si noti che il regex deve corrispondere alla riga corrispondente in entrambi i file e deve corrispondere a tutte le linee modificate nel pezzo per funzionare, altrimenti mostrerà comunque la differenza.

Utilizzare le virgolette singole per proteggere il pattern dall'espansione di Shell e per sfuggire ai caratteri riservati alla regex (ad es. Parentesi quadre).

Possiamo leggere in diffutils manuale:

Però, -I ignora l'inserimento o l'eliminazione di righe che contengono l'espressione regolare se ogni riga modificata nell'hunk (ogni inserimento e ogni eliminazione) corrisponde all'espressione regolare.

In altre parole, per ogni cambiamento non ignorabile, diff stampa l'insieme completo di cambiamenti nelle sue vicinanze, inclusi quelli ignorabili. Puoi specificare più di un'espressione regolare per le righe da ignorare utilizzando più di una -I opzione. diff tenta di far corrispondere ciascuna riga a ciascuna espressione regolare, iniziando dall'ultima indicata.

Questo comportamento è anche ben spiegato da armel qui .

Correlati: Come posso eseguire un diff che ignora tutti i commenti?

7
kenorb

Dopo aver cercato sul Web, il modo alternativo di Lekensteyn è il migliore che ho trovato.

Ma voglio usare l'output dif come patch ... e c'è un problema perché il numero di riga viene tenuto presente a causa di "grep -v".

Quindi intendo migliorare questa riga di comando:

diff -u -B <(sed 's/^[[:blank:]]*#.*$/ /' file1)  <(sed 's/^[[:blank:]]*#.*$/ /' file2)

Non è perfetto ma il numero di riga viene mantenuto nel file patch.

Tuttavia, se viene aggiunta una nuova riga anziché la riga del commento ... il commento produrrà un Hunk FAILED durante la correzione, come possiamo vedere qui sotto.

File test1:
  text
  #comment
  other text
File test2:
  text
  new line here
  #comment changed
  other text changed

prova ora il nostro comando

$ echo -e "#!/usr/bin/sed -f\ns/^[[:blank:]]*#.*$/ /" > outcom.sed
$ echo "diff -u -B <(./outcom.sed \$1)  <(./outcom.sed \$2)" > mydiff.sh
$ chmod +x mydiff.sh outcom.sed
$ ./mydiff.sh file1 file2 > file.dif
$ cat file.dif
--- /dev/fd/63  2014-08-23 10:05:08.000000000 +0200
+++ /dev/fd/62  2014-08-23 10:05:08.000000000 +0200
@@ -1,2 +1,3 @@
 text
+new line

-other text
+other text changed

/ dev/fd/62 &/dev/fd/63 sono file prodotti dalla sostituzione del processo. La linea tra "+ nuova riga" e "-altro testo" è il carattere di spazio predefinito definito nella nostra espressione sed per sostituire i commenti.

E ora, cosa succede quando applichiamo questa patch:

$ patch -p0 file1 < file.dif 
patching file file1
Hunk #1 FAILED at 1.
1 out of 1 hunk FAILED -- saving rejects to file file1.rej

La soluzione è non usare il formato diff unificato senza -u

$ echo "diff -B <(./outcom.sed \$1)  <(./outcom.sed \$2)" > mydiff.sh
$ ./mydiff.sh file1 file2 > file.dif
$ cat file.dif
1a2
> new line
3c4
< other text
---
> other text changed
$ patch -p0 file1 < file.dif 
patching file file1
$ cat file1
text
new line
#comment
other text changed

ora patch file di lavoro file (senza garanzia di risultati in un processo diff molto complesso).

3
syjust

Di solito ignoro questo disordine per entrambi:

  • Generazione di versioni non commentate utilizzando grep -v "^#" | cat -s e differendo quelli o ...
  • Utilizzando vim -d per guardare i file. L'evidenziazione della sintassi si occupa di rendere le differenze tra commenti e non commenti abbastanza ovvie. L'evidenziazione differenziale della differenza in linea in modo da poter vedere quali valori o parti di valori sono stati modificati a colpo d'occhio rende questo il mio preferito.
1
Caleb

Ecco cosa uso per rimuovere tutte le righe commentate, anche quelle che iniziano con una scheda o uno spazio, e quelle vuote:

egrep -v "^$|^[[:space:]]*#" /path/to/file

o puoi farlo

sed -e '/^#.*/d' -e 's/#.*//g' | cat -s
0
Philomath