Esiste uno strumento per ottenere le righe in un file che non si trovano in un altro?


Risposte:


159

Sì. Lo grepstrumento standard per la ricerca di stringhe di testo nei file può essere utilizzato per sottrarre tutte le righe in un file da un altro.

grep -F -x -v -f fileB fileA

Funziona usando ogni riga in fileB come un pattern ( -f fileB) e trattandola come una stringa semplice per abbinare (non una regex normale) ( -F). Imponi che la corrispondenza avvenga sull'intera riga ( -x) e stampi solo le righe che non corrispondono ( -v). Pertanto si stanno stampando le righe nel file A che non contengono gli stessi dati di qualsiasi riga nel file B.

Il rovescio della medaglia di questa soluzione è che non tiene conto dell'ordine delle righe e se il tuo input ha righe duplicate in posti diversi potresti non ottenere quello che ti aspetti. La soluzione è quella di utilizzare un vero strumento di confronto come diff. Puoi farlo creando un file diff con il valore di contesto al 100% delle righe nel file, quindi analizzandolo solo per le righe che verrebbero rimosse se si converte il file A in file B. (Nota: questo comando rimuove anche il diff formattazione dopo che ottiene le linee giuste.)

diff -U $(wc -l < fileA) fileA fileB | sed -n 's/^-//p' > fileC

@ inderpreet99 L' -uargomento in minuscolo accetta effettivamente un parametro di un numero purché non sia seguito da uno spazio. Il vantaggio del modo in cui l'ho avuto prima è che funzionerà con o senza un valore, quindi potresti usare qualcosa in quella routine di comando secondario che non ha restituito output. D'altra parte '-U' richiede invece un argomento.
Caleb,

stare attenti, grep -f è O (N ^ 2) Credo: stackoverflow.com/questions/4780203/...
rogerdpack

1
la diffpipeline funziona a meraviglia grazie.
Felipe Alvarez,

Per tenere conto del problema di ordinamento, è possibile utilizzare la sostituzione del processo nel comando per elaborare ciascun file prima del grepnecessario. Esempio:grep -F -x -v -f <(sort fileB) <(sort fileA)
Tony Cesaro,

@TonyCesaro Funzionerebbe se il tuo set di dati non è specifico per l'ordine e i duplicati non devono essere presi in considerazione. Il vantaggio dell'uso diffè che la posizione nel file viene presa in considerazione.
Caleb,

57

La risposta dipende molto dal tipo e dal formato dei file che stai confrontando.

Se i file che stai confrontando sono file di testo ordinati, lo strumento GNU scritto da Richard Stallman e Davide McKenzie chiamato commpotrebbe eseguire il filtro che stai cercando. Fa parte dei coreutils.

Esempio

Supponi di avere i seguenti 2 file:

$ cat a
1
2
3
4
5

$ cat b
1
2
3
4
5
6

Linee nel file bche non sono nel file a:

$ comm <(sort a) <(sort b) -3
    6

1
+1 per la menzione comm; sfortunatamente, commrichiede file ordinati
Arcege,

11
quindi ordinali? comm <(ordina a) <(ordina b) -1 -2
Sirex,

Questa è una strana sintassi. <()? Funziona e lo capisco, ma c'è un nome per questa stranezza?
mlissner,

2
@mlissner <()è anche noto come sostituzione del processo .
Miku,

1
commfu originariamente scritto intorno al 1973 da qualcuno di Bell Labs, non da rms. Ti riferisci all'implementazione GNU che è arrivata molto più tardi. Nel corso degli anni ci sono state molte diverse implementazioni delle utility Unix.
Stéphane Chazelas,

32

dallo stackoverflow ...

comm -23 file1 file2

-23 elimina le righe che si trovano in entrambi i file o solo nel file 2. I file devono essere ordinati (sono nel tuo esempio) ma, in caso contrario, esegui il pipe prima attraverso l'ordinamento ...

Vedi la pagina man qui


Questo non funziona per me, per qualche ragione ...
Jan

@Jan sono i tuoi file ordinati? Come li hai ordinati?
JJS

8

I metodi grep e comm (con ordinamento) richiedono molto tempo su file di grandi dimensioni. SiegeX e ghostdog74 hanno condiviso due grandi metodi awk per l'estrazione di righe uniche per uno dei due file su Stack Overflow:

$ awk 'FNR==NR{a[$0]++}FNR!=NR && !a[$0]{print}' file1 file2

$ awk 'FNR==NR{a[$0]++;next}(!($0 in a))' file1 file2

2
Se lo stai facendo con file di grandi dimensioni, i limiti di memoria del caricamento di un file di grandi dimensioni in un array associativo saranno proibitivi.
Charles Duffy,

4

Se i file sono grandi e non hai un ordine personalizzato per le tue voci, grep impiega troppo tempo. Una rapida alternativa sarebbe

sort file1 > 1 
sort file2 > 2 
diff 1 2 | grep "\>" | sed -e 's/> //'

[file2-file1 risultati su schermo, pipe su file ecc.]

Passando >a <si otterrebbe la sottrazione opposta.rm 1 2


2

Potresti anche considerare vimdiff, evidenzia le differenze tra i file in un editor vim


1
Ma c'è un modo semplice per fare automaticamente la sottrazione in Vimdiff?
Kazark
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.