Come posso ottenere diff per mostrare solo le linee aggiunte ed eliminate? Se diff non può farlo, quale strumento può farlo?


69

Come posso ottenere diff per mostrare solo le linee aggiunte ed eliminate? Se diff non può farlo, quale strumento può farlo?


2
Devi definire meglio cosa intendi con aggiunta ed eliminazione. In particolare, una linea può cambiare? In tal caso, come si desidera gestire una linea modificata? Se si sta eseguendo un controllo rigorosamente orientato alla linea, una modifica della linea è identica alla vecchia linea da rimuovere e alla nuova linea da aggiungere. Ad esempio, come dovrebbe gestire una linea divisa in due? Come sono cambiate due 1 riga? 2 righe sono cambiate? 1 riga rimossa e 2 righe aggiunte? A meno che tu non possa garantire che le linee non cambieranno mai, saranno solo aggiunte ed eliminate, penso che questo è destinato a fallire senza definizioni migliori.
Christopher Cashell,

Trovo la domanda abbastanza poco chiara. Ma almeno un'interpretazione della domanda potrebbe essere risolta condiff A B | grep '^[<>]'
kasperd,

Forse stai cercando comm.
Jenny D dice Reinstate Monica l'

@ChristopherCashell, intende ignorare l'ordinamento; un problema tipicamente comune. Di solito questo viene fatto ordinando prima i segmenti (linee) su ciascun lato prima di fare un diff diff.
Pacerier

@Pacerier, ne sei sicuro? O stai indovinando? Nulla sull'ordinamento o sull'ordine di ricerca è menzionato o suggerito nella domanda. Allo stato attuale, la domanda non è chiara e potrebbe essere interpretata in molti modi diversi. Senza sapere con certezza cosa sta chiedendo, stiamo formulando ipotesi e offrendo soluzioni che potrebbero risolvere il problema. Inoltre, il commento del poster originale su una delle risposte suggerisce che questo non è correlato all'ordinamento. Ha a che fare con il significato di "aggiunto ed eliminato" vs. "cambiato".
Christopher Cashell,

Risposte:


82

Prova comm

Un altro modo di vederlo:

  • Mostra le righe che esistono solo nel file a: (ovvero ciò che è stato eliminato da a)

    comm -23 a b
    
  • Mostra le righe che esistono solo nel file b: (ovvero ciò che è stato aggiunto a b)

    comm -13 a b
    
  • Mostra le righe che esistono solo in un file o nell'altro: (ma non entrambi)

    comm -3 a b | sed 's/^\t//'
    

(Avviso: se il file aha righe che iniziano con TAB, esso (il primo TAB) verrà rimosso dall'output.)

Solo file ordinati

NOTA: entrambi i file devono essere ordinati per commfunzionare correttamente. Se non sono già ordinati, è necessario ordinarli:

sort <a >a.sorted
sort <b >b.sorted
comm -12 a.sorted b.sorted

Se i file sono estremamente lunghi, questo può essere piuttosto oneroso in quanto richiede una copia aggiuntiva e quindi il doppio dello spazio su disco.


5
volevo solo aggiungere che entrambi i file devono essere ordinati (con distinzione tra maiuscole e minuscole) affinché questa soluzione produca risultati corretti
marmor,

1
Su shell abbastanza moderne, puoi ordinare in linea con qualcosa di similecomm -12 <(sort a) <(sort b)
Joshua Huber,

14

commpotrebbe fare quello che vuoi. Dalla sua pagina man:

DESCRIZIONE

Confronta i file ordinati FILE1 e FILE2 riga per riga.

Senza opzioni, produce output a tre colonne. La colonna uno contiene righe univoche per FILE1, la colonna due contiene righe univoche per FILE2 e la colonna tre contiene righe comuni a entrambi i file.

Queste colonne sono suppressable con -1, -2e -3rispettivamente.

Esempio:

[root@dev ~]# cat a
common
shared
unique

[root@dev ~]# cat b
common
individual
shared

[root@dev ~]# comm -3 a b
    individual
unique

E se vuoi solo le linee uniche e non ti importa in quale file si trovano:

[root@dev ~]# comm -3 a b | sed 's/^\t//'
individual
unique

Come dice la pagina man, i file devono essere ordinati in anticipo.


9

Per mostrare aggiunte ed eliminazioni senza contesto, numeri di riga, +, -, <,>! ecc., puoi usare diff in questo modo:

diff --changed-group-format='%<%>' --unchanged-group-format='' a.txt b.txt 

Ad esempio, dati due file:

a.txt

Common
Common
A-ONLY
Common

b.txt

Common
B-ONLY
Common
Common

Il comando seguente mostrerà le linee rimosse da a o aggiunte a b:

diff --changed-group-format='%<%>' --unchanged-group-format='' a.txt b.txt 

produzione:

B-ONLY
A-ONLY

Questo comando leggermente diverso mostrerà le linee rimosse da a.txt:

diff --changed-group-format='%<' --unchanged-group-format='' a.txt b.txt 

produzione:

A-ONLY

Infine, questo comando mostrerà le linee aggiunte ad a.txt

diff --changed-group-format='%>' --unchanged-group-format='' a.txt b.txt 

produzione

B-ONLY

2

Questo è ciò che fa diff per impostazione predefinita ... Forse devi aggiungere alcuni flag per ignorare gli spazi bianchi?

diff -b -B

dovrebbe ignorare le righe vuote e diversi numeri di spazi.


1
No, mostra anche le linee CAMBIATE (linee che hanno un carattere o quattro diverse). Voglio linee che esistono solo a sinistra o a destra.
C. Ross,

2
Si potrebbe sostenere che le diverse versioni di un file CHANGED esistono ciascuna solo a sinistra o a destra.
markdrayton,

2
Non c'è modo per diff (o qualsiasi altro strumento) di dire in modo affidabile cosa è un cambiamento e che cos'è una linea eliminata che viene sostituita da una nuova linea.
Cian,

1
Tecnicamente, diff tratta una linea "modificata" come se la linea originale fosse eliminata e fosse aggiunta una nuova linea ... quindi tecnicamente ti mostra solo le linee aggiunte ed eliminate.
KFro

2

No, in diffrealtà non mostra le differenze tra due file nel modo in cui uno potrebbe pensare. Produce una sequenza di comandi di modifica per uno strumento come patchutilizzare per cambiare un file in un altro.

La difficoltà per qualsiasi tentativo di fare ciò che stai cercando è come definire ciò che costituisce una linea che è cambiata rispetto a una cancellata seguita da una aggiunta. Anche cosa fare quando le linee vengono aggiunte, eliminate e modificate l'una accanto all'altra.


I miei pensieri esattamente. Quale percentuale di caratteri in una riga deve cambiare per considerarla nuova anziché una modifica dell'originale? Tecnicamente, anche se hai un personaggio in comune, potresti considerarlo un "cambiamento" anziché una cancellazione e un inserimento.
Kamil Kisiel,

1
È da molto tempo che non guardo alle difffonti, ma mi sembra di ricordare tutti i tipi di rotazioni per tenere traccia di dove due file corrispondono per rimanere sincronizzati e penso che ci sia una soglia per rinunciare in base alla distanza le linee sono. Ma non ricordo alcuna corrispondenza intra-line tranne che per lo spazio bianco (facoltativamente) compresso o ignorando il caso. O (forse) parole per quell'effetto. In ogni caso, si tratta solo di patch"vgrep". Può essere. Martedì.
Dennis Williamson,

2

Gli strumenti di confronto visivo adattano due file insieme in modo che un segmento con lo stesso numero di linee ma con contenuto diverso verrà considerato un segmento modificato. Le linee completamente nuove tra i segmenti corrispondenti sono considerate segmenti aggiunti.

Questo è anche il modo in cui funziona lo strumento da riga di comando sdiff , che mostra un confronto fianco a fianco di due file in un terminale. Le righe modificate sono separate da | carattere. Se esiste una riga solo nel file A, <viene utilizzato come carattere di separazione. Se esiste una riga solo nel file B,> viene utilizzato come separatore. Se non hai <e> caratteri nei file, puoi usarlo per mostrare solo le righe aggiunte:

sdiff A B | grep '[<>]'

2

Grazie senarvi, la tua soluzione (non votata per) in realtà mi ha dato ESATTAMENTE quello che volevo dopo aver cercato per anni su una tonnellata di pagine.

Usando la tua risposta, ecco cosa mi è venuto in mente per far cambiare / aggiungere / cancellare l'elenco delle cose. L'esempio utilizza 2 versioni del file / etc / passwd e stampa il nome utente per i record pertinenti.

#!/bin/bash
sdiff passwd1 passwd2 | grep '[|]' | awk -F: '{print "changed: " $1}'
sdiff passwd1 passwd2 | grep '[<]' | awk -F: '{print "deleted: " $1}'
sdiff passwd1 passwd2 | grep '[>]' | awk -F\> '{print $2}' | awk -F: '{print "added: " $1}'

Si noti che poiché la differenza tra "una linea è stata modificata" e "una linea è stata rimossa e un'altra linea è stata aggiunta sotto o sopra di essa" è semantica. Uno strumento diff generico basato su testo non può separare questi casi. Di conseguenza, la risposta basata su sdiff non può funzionare in modo affidabile per tutti i casi.
Mikko Rantalainen,

0

Trovo questa forma particolare spesso utile:

diff --changed-group-format='-%<+%>' --unchanged-group-format='' f g

Esempio:

printf 'a\nb\nc\nd\ne\nf\ng\n' > f
printf 'a\nB\nC\nd\nE\nF\ng\n' > g
diff --old-line-format=$'-%l\n' \
     --new-line-format=$'+%l\n' \
     --unchanged-line-format='' \
     f g

Produzione:

-b
-c
+B
+C
-e
-f
+E
+F

Quindi mostra le vecchie linee con -seguite immediatamente dalla corrispondente nuova linea con +.

Se avessimo cancellato C:

printf 'a\nb\nd\ne\nf\ng\n' > f
printf 'a\nB\nC\nd\nE\nF\ng\n' > g
diff --old-line-format=$'-%l\n' \
     --new-line-format=$'+%l\n' \
     --unchanged-line-format='' \
     f g

sembra così:

-b
+B
+C
-e
-f
+E
+F

Il formato è documentato su man diff:

       --line-format=LFMT
              format all input lines with LFMT`

e:

       LTYPE is 'old', 'new', or 'unchanged'.
              GTYPE is LTYPE or 'changed'.

e:

              LFMT (only) may contain:

       %L     contents of line

       %l     contents of line, excluding any trailing newline

       [...]

Domanda correlata: https://stackoverflow.com/questions/15384818/how-to-get-the-difference-only-additions-between-two-files-in-linux

Testato su Ubuntu 18.04.


-1

file1:

text670_1
text067_1
text067_2

file2:

text04_1
text04_2
text05_1
text05_2
text067_1
text067_2
text1000_1

Uso:

diff -y file1 file2

Questo mostra due colonne per i file ripetitivi.

Produzione:

text670_1                           
                                  > text04_1
                                  > text04_2
                                  > text05_1
                                  > text05_2
text067_1                           text67_1
text067_2                           text67_2
                                  > text1000_1
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.