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


55

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 diffi 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) sarebbe saltare il primo requisito #.*. Ho provato l' opzione --ignore-matching-lines=RE( -I RE) di GNU diff 3.0, ma non sono riuscito a farlo funzionare con quella RE. Ho anche provato .*#.*e .*\#.*senza fortuna. Mettere letteralmente la linea ( Port 631) in quanto REnon corrisponde a nulla, né aiuta a mettere la RE tra le barre.

Come suggerito nel sapore di regex dello strumento "diff" sembra mancare? , Ho provato grep -G:

grep -G '#.*' file

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

Quindi, come dovrebbe essere usata questa opzione? Come posso fare per diffsaltare alcune righe (nel mio caso, commenti)? Non suggerire di grepinserire il file e di confrontare i file temporanei.


12
L' -Iopzione fa sì che un blocco venga ignorato solo se tutte le sue linee corrispondono alla regexp. Quindi puoi ignorare una modifica di solo commento in quel modo, ma non le modifiche di commento che si avvicinano a una modifica di non commento.
Gilles 'SO- smetti di essere malvagio' il

@Gilles: Grazie, ora capisco perché diff -Inon si comporta come mi aspettavo. Ho aggiornato la mia risposta con un esempio che ha chiarito questo comportamento per me.
Lekensteyn,

Risposte:


49

Secondo Gilles, l' -Iopzione ignora una linea 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 -Icorrettamente l' opzione, fornirò un'alternativa che funziona con 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, eliminando la necessità di un file temporaneo
  • grep - comando per la stampa di linee (non) corrispondenti a un motivo
    • -v - mostra linee non corrispondenti
    • E - usa espressioni regolari estese
    • '^\s*(#|$)' - un'espressione regolare corrispondente a commenti e righe vuote
      • ^ - corrisponde all'inizio di una riga
      • \s* - abbina spazi bianchi (schede e spazi) se presenti
      • (#|$) abbina un segno di hash o, in alternativa, la fine di una linea

6

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 nell'hunk per funzionare, altrimenti mostrerà comunque la differenza.

Usa le virgolette singole per proteggere il pattern dall'espansione della shell e per sfuggire ai caratteri riservati alla regex (es. Parentesi).

Possiamo leggere nel diffutilsmanuale:

Tuttavia, -Iignora solo 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, diffstampa l'insieme completo di cambiamenti nelle sue vicinanze, compresi quelli ignorabili. È possibile specificare più di un'espressione regolare per le righe da ignorare utilizzando più di -Iun'opzione. diffcerca di far corrispondere ciascuna riga a ciascuna espressione regolare, iniziando dall'ultima data.

Questo comportamento è anche ben spiegato da Armel qui .

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


2

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 il patching, 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 e / 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).


Il tuo diff unificato non si applica a causa delle differenze di contesto. È possibile utilizzare diff -U0 one twoper disabilitare il contesto. Per le patch, ci sono un sacco di strumenti che potrebbero essere più adatti come kdiff3.
Lekensteyn,

Grazie per l' -U0opzione per disabilitare il contesto. Nota: kdiff3 è uno strumento grafico. Ho bisogno di uno strumento automatico per gestire gli attributi git merge.
sabato

vimdiffsupporta fusioni a tre vie, vale la pena guardare.
Lekensteyn,

per essere più precisi, ho bisogno di uno strumento di script per automatizzare il processo di unione git con gli exludes in uno script sql. kdiff3 e vimdiff sono strumenti interattivi, non utilizzabili nel mio caso.
Syjust,

1

Di solito ignoro questo disordine per entrambi:

  • Generare versioni non commentate usando grep -v "^#" | cat -se diffondendo quelle o ...
  • Usando vim -dper 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.

0

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.