come mostrare le linee in comune (reverse diff)?


170

Ho una serie di file di testo per i quali mi piacerebbe conoscere le linee in comune piuttosto che le linee che sono diverse tra loro. La riga di comando unix o windows va bene.

pippo:

linux-vdso.so.1 =>  (0x00007fffccffe000)
libvlc.so.2 => /usr/lib/libvlc.so.2 (0x00007f0dc4b0b000)
libvlccore.so.0 => /usr/lib/libvlccore.so.0 (0x00007f0dc483f000)
libc.so.6 => /lib/libc.so.6 (0x00007f0dc44cd000)

bar:

libkdeui.so.5 => /usr/lib/libkdeui.so.5 (0x00007f716ae22000)
libkio.so.5 => /usr/lib/libkio.so.5 (0x00007f716a96d000)
linux-vdso.so.1 =>  (0x00007fffccffe000)

Quindi, dato che questi due file sopra l'output dell'utilità desiderata sarebbero simili file1:line_number, file2:line_number == matching text (solo un suggerimento, non mi interessa davvero quale sia la sintassi):

foo:1, bar:3 == linux-vdso.so.1 =>  (0x00007fffccffe000)

Grazie.


@ChristopherSchultz Il mio errore. La prima riga del 1 ° esempio dovrebbe corrispondere all'ultima riga del 2 ° esempio. Grazie per aver colto l'errore; mutevole.
matt wilkie,

1
Un'altra domanda simile con buone risposte: unix.stackexchange.com/questions/1079/…
MortezaE

Risposte:


210

Su * nix, puoi usare comm . La risposta alla domanda è:

comm -1 -2 file1.sorted file2.sorted 
# where file1 and file2 are sorted and piped into *.sorted

Ecco il pieno utilizzo di comm:

comm [-1] [-2] [-3 ] file1 file2
-1 Suppress the output column of lines unique to file1.
-2 Suppress the output column of lines unique to file2.
-3 Suppress the output column of lines duplicated in file1 and file2. 

Si noti inoltre che è importante ordinare i file prima di utilizzare comm, come indicato nelle pagine man.


3
comm [-1] [-2] [-3] file1 file2 -1 Elimina la colonna di output delle righe univoche per file1. -2 Sopprime la colonna di output delle righe univoche per file2. -3 Sopprime la colonna di output delle righe duplicate in file1 e file2.
ojblass,

@ojblass: aggiunto questo alla risposta.
Matt J,

6
Ho scoperto che è importante ordinare i file prima di utilizzare comm. Forse aggiungilo alla risposta.
matt wilkie,

11
risposta breve alla domanda: comm -1 -2 file1 file2
greggles

6
Puoi usarlo se i tuoi file non sono ordinati: comm -1 -2 <(ordina nome file1) <(ordina nome file2)
Kevin Wheeler

56

Ho trovato questa risposta su una domanda elencata come duplicata . Trovo grep più facile da gestire rispetto a comm, quindi se vuoi solo il set di linee corrispondenti (utile per confrontare CSV, per esempio) usa semplicemente

grep -F -x -f file1 file2

o la versione semplificata di fgrep

fgrep -xf file1 file2

Inoltre, puoi usare file2* glob e cercare linee in comune con più file, anziché solo due.

Alcune altre utili varianti includono

  • -n flag per mostrare il numero di riga di ogni riga abbinata
  • -c per contare solo il numero di righe corrispondenti
  • -vper visualizzare solo le righe in file2 che differiscono (o usano diff).

L'uso commè più veloce, ma quella velocità ha il costo di dover prima ordinare i file. Non è molto utile come "inversa diff".


grazie Ryder, questo potrebbe essere più utile della comunicazione a molti. Dovresti collegarti alla risposta della fonte (ci sono più di una mezza dozzina collegati in Q nel menu di navigazione a destra; è un po 'di lavoro da trovare). Sarebbe anche bello sapere quanto bene grep fa con input non ordinati o diversamente ordinati e come stampare i rispettivi numeri di riga delle corrispondenze.
matt wilkie,

1
@mattwilkie Ho sentito il bisogno di tornare indietro e chiarire l'uso della -vbandiera dopo essermi infilato da solo. Supponi di avere due file CSV file1 e file2 e che abbiano righe sia sovrapposte che non sovrapposte. Se si desidera tutte e solo le righe non sovrapposte, l'utilizzo fgrep -v file1 file2restituirà solo le righe non sovrapposte in file2 e nessuna delle righe aggiuntive non sovrapposte in file1 . Questo può essere ovvio per alcuni, ma meglio affermare l'ovvio che il rischio di interpretazioni errate. In questo caso particolare, l'ordinamento dei file e l'utilizzo commè ancora la scelta migliore.
Ryder,

1
Grazie per essere tornato e aver chiarito Ryder. L'attenzione in più è notata e apprezzata (tutto è così facile far scivolare via le cose vecchie!). Ho scambiato la risposta accettata perché comm è chiaramente la scelta della comunità, anche se personalmente la uso ancora quando l'ordinamento è sovraccarico indesiderato.
matt wilkie,

2
Un'altra complicazione durante l'utilizzo grep: qualsiasi riga vuota nel primo file corrisponderà a ogni riga nel secondo file. Assicurati che file1non ci siano righe vuote, o sembrerà che i file siano identici.
Christopher Schultz,

grep -Fxfè per me.
Loxaxs

35

È stato chiesto qui prima: comando Unix per trovare linee comuni in due file

Puoi anche provare con perl (il credito va qui )

perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/'  file1 file2

1
Grazie. Mi piacerebbe accettare entrambe le risposte, dato che il rivestimento del perl è cross platform. Comm ottiene il cenno del capo perché è più semplice.
matt wilkie,

1
Perfetto. Utilizzando il terminale Cygwin su Windows e commnon era prontamente disponibile. Questa era l'alternativa perfetta.
Qix - MONICA È STATA MISTREATA il

3
Questo non importa come vengono ordinate le linee. È più preciso della comunicazione.
enl8enmentnow,


17

Ho appena imparato il comando comm da questo thread, ma volevo aggiungere qualcosa in più: se i file non sono ordinati e non si desidera toccare i file originali, è possibile reindirizzare l'output del comando sort. Questo lascia intatti i file originali. Funziona a bash, non posso dire di altre shell.

comm -1 -2 <(sort file1) <(sort file2)

Questo può essere esteso per confrontare l'output del comando, anziché i file:

comm -1 -2 <(ls /dir1 | sort) <(ls /dir2 | sort)

9

Il modo più semplice per fare è:

awk 'NR==FNR{a[$1]++;next} a[$1] ' file1 file2

I file non sono necessari per essere ordinati.


1
Ciò è diverso dalla maggior parte delle risposte qui in quanto consente di ricostruire modelli di origine. Ho due file creati dallo stesso wrapper, con testo diverso inserito in alcuni punti. Questa risposta mi ha permesso di recuperare il wrapper.
Lucas Gonze,

1

Solo per informazione, ho creato un piccolo strumento per Windows che fa la stessa cosa di "grep -F -x -f file1 file2" (dato che non ho trovato nulla di equivalente a questo comando su Windows)

Eccolo qui: http://www.nerdzcore.com/?page=commonlines

L'utilizzo è "CommonLines inputFile1 inputFile2 outputFile"

È disponibile anche il codice sorgente (GPL)


1

In di Windows è possibile utilizzare un PowerShell script con CompareObject

compare-object -IncludeEqual -ExcludeDifferent -PassThru (get-content A.txt) (get-content B.txt)> MATCHING.txt | Out-Null #Find Matching Lines

CompareObject:

  • IncludeEqual senza -ExcludeDifferent: tutto
  • ExcludeDifferent without -InclueEqual: Nothing
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.