Differenza di due file di grandi dimensioni


14

Ho "test1.csv" e contiene

200,400,600,800
100,300,500,700
50,25,125,310

e test2.csv e contiene

100,4,2,1,7
200,400,600,800
21,22,23,24,25
50,25,125,310
50,25,700,5

adesso

diff test2.csv test1.csv > result.csv

è diverso da

diff test1.csv test2.csv > result.csv

Non so quale sia l'ordine corretto ma voglio qualcos'altro, entrambi i comandi sopra genereranno qualcosa di simile

2 > 100,4,2,1,7
   3 2,3c3,5
   4 < 100,300,500,700
   5 < 50,25,125,310
   6 \ No newline at end of file
   7 ---
   8 > 21,22,23,24,25
   9 > 50,25,125,310

Voglio produrre solo la differenza, quindi results.csv dovrebbe apparire così

100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5

Ci ho provato diff -qe diff -snon hanno fatto il trucco. L'ordine non importa, ciò che conta è che voglio vedere solo la differenza, no> né <né spazio vuoto.

grep -FvF ha fatto il trucco su file più piccoli, non su file grandi

il primo file contiene più di 5 milioni di righe, il secondo file contiene 1300.

quindi results.csv dovrebbe comportare ~ 4.998.700 righe

Ho anche provato grep -F -x -v -f che non ha funzionato.



1
@Tim ho visto il tuo link e sono un vecchio membro, quindi conosco le regole ma sono stato negligente, scusa :) lo stavo modificando e ho visto un popup che il post è stato modificato, quindi hai fatto il lavoro per me e sono grato signore.
Lynob,

50,25,125,310è comune ad entrambi i file .. è necessario rimuoverlo dall'output desiderato ..
heemayl

L'ordine dovrebbe essere preservato?
kos,

1
dipende da cosa vuoi fare con le informazioni, diff, IMO, è per fare una patch. Ad ogni modo, ora sono sicuro del tuo miglior strumento, diff, grep, awk o perl.
Pantera il

Risposte:


20

Sembra un lavoro per comm:

$ comm -3 <(sort test1.csv) <(sort test2.csv)
100,300,500,700
    100,4,2,1,7
    21,22,23,24,25
    50,25,700,5

Come spiegato in man comm:

   -1     suppress column 1 (lines unique to FILE1)

   -2     suppress column 2 (lines unique to FILE2)

   -3     suppress column 3 (lines that appear in both files)

Quindi, i -3mezzi che verranno stampati solo le linee che sono univoci per uno dei file. Tuttavia, quelli sono rientrati in base al file in cui sono stati trovati. Per rimuovere la scheda, utilizzare:

$ comm -3 <(sort test1.csv) <(sort test2.csv) | tr -d '\t'
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5

In questo caso, non è nemmeno necessario ordinare i file e puoi semplificare quanto sopra per:

comm -3 test1.csv test2.csv | tr -d '\t' > difference.csv

Non sei stato ingannato dagli spazi dopo la 200,[...]linea eh? :)
kos,

@kos no, ho rimosso prima gli spazi finali dai file. Presumo che i file dell'OP non li abbiano effettivamente.
terdon,

6

Utilizzo grepcon bashsostituzione processo:

$ cat <(grep -vFf test2.csv test1.csv) <(grep -vFf test1.csv test2.csv)
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5

Per salvare l'output come results.csv:

cat <(grep -vFf test2.csv test1.csv) <(grep -vFf test1.csv test2.csv) >results.csv
  • <()è il modello di sostituzione del bashprocesso

  • grep -vFf test2.csv test1.csv troverà le linee uniche per solo test1.csv

  • grep -vFf test1.csv test2.csv troverà le linee uniche per solo test2.csv

  • Infine stiamo riassumendo i risultati per cat

O come ha suggerito Oli , puoi usare anche il raggruppamento dei comandi:

$ { grep -vFf test2.csv test1.csv; grep -vFf test1.csv test2.csv; }
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5

O semplicemente esegui uno dopo l'altro, poiché entrambi scrivono su STDOUT, alla fine verranno aggiunti:

$ grep -vFf test2.csv test1.csv; grep -vFf test1.csv test2.csv
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5

1
Perché catdue comandi reindirizzati? Perché non eseguire solo uno poi l'altro? grep ... ; grep ...o { grep ... ; grep ... ; }se volessi fare qualcosa con l'output collettivo.
Oli

@Oli Grazie..è un'ottima idea..non ci ho pensato ..
heemayl

4

Se l'ordine delle righe non è pertinente, utilizzare awko perl:

awk '{seen[$0]++} END {for (i in seen) {if (seen[i] == 1) {print i}}}' 1.csv 2.csv

Utilizzare grepper ottenere le linee comuni e filtrarle:

grep -hxvFf <(grep -Fxf 1.csv 2.csv) 1.csv 2.csv

Il grep interno ottiene le linee comuni, quindi il grep esterno trova linee che non corrispondono a queste linee comuni.


Il tuo comando awk reimplementa sort | uniq -u, il che dà la risposta sbagliata quando un file contiene righe duplicate. Per grep, direi "interno" / "esterno", non "interno" / "esterno".
Peter Cordes,

@PeterCordes sì, lo fa e chi sei tu per dire che è il risultato sbagliato?
muru,

Sbagliato nel senso che non è esattamente quello che la domanda ha posto, in quel caso d'angolo. Potrebbe essere quello che qualcuno vuole, ma si dovrebbe notare la differenza tra ciò che il vostro awksarà stampare e ciò che l' comm -3e diffrisposte stamperà.
Peter Cordes,

@PeterCordes di nuovo, chi sei tu per dirlo? Fino a quando l'OP dice che è quello che vogliono, non mi interessa se l'output differisce da quello di comm -3. Non vedo alcun motivo per cui dovrei spiegarlo. Se vuoi modificare una nota, sentiti libero.
muru,

L'OP ha detto che vuole la differenza. Non è sempre quello che produce il tuo programma. Un programma che produce lo stesso output per un testcase, ma non soddisfa la descrizione scritta per tutti i casi, richiede un avviso. Sono qui per dirlo, ed è vero indipendentemente da chi sono o da chi sei. Ho aggiunto una nota
Peter Cordes,

4

Usa le --*-line-format=...opzioni didiff

Puoi dire diffesattamente ciò di cui hai bisogno - spiegato di seguito:

diff --old-line-format='%L' --new-line-format='%L' --unchanged-line-format='' f1.txt f2.txt

È possibile specificare l'output di diff in modo molto dettagliato, simile a un printfformato numerico.

Le linee dal primo file, test1.csvchiamate linee "vecchie", e le linee dal secondo test2.csv, sono linee "nuove". Questo ha senso quando diffviene utilizzato per vedere cosa è cambiato in un file.

Le opzioni di cui abbiamo bisogno sono quelle per impostare il formato per "vecchie" linee, "nuove" linee e "invariate".
I formati di cui abbiamo bisogno sono molto semplici:
per le righe modificate, nuove e vecchie, vogliamo produrre solo il testo delle righe. %Lè il simbolo del formato per il testo della riga.
Per le linee invariate, non vogliamo mostrare nulla.

Con questo, possiamo scrivere opzioni come --old-line-format='%L'e mettere tutto insieme, usando i tuoi dati di esempio:

$ diff --old-line-format='%L' --new-line-format='%L' --unchanged-line-format='' test1.csv test2.csv
100,4,2,1,7
100,300,500,700
21,22,23,24,25
50,25,700,5


Note sulle prestazioni

Poiché i file hanno dimensioni diverse, prova a scambiare i file di input se non ha importanza, potrebbe essere che il funzionamento interno di diffpuò gestire in un modo migliore dell'altro. Meglio o richiede meno memoria o meno calcoli.

C'è un'opzione di ottimizzazione per l'utilizzo diffcon i file di grandi dimensioni: --speed-large-files. Utilizza ipotesi sulla struttura del file, quindi non è chiaro se aiuta nel tuo caso, ma vale la pena provarlo.

Le opzioni di formato sono descritte di man diffseguito --LTYPE-line-format=LFMT.


3

Poiché l'ordine non ha bisogno di essere preservato, semplicemente:

sort test1.csv test2.csv | uniq -u
  • sort test1.csv test2.csv: unisce e ordina test1.csvetest2.csv
  • uniq -u: stampa solo le linee che non hanno duplicati

Ciò non funziona se un file contiene una riga due volte, che non viene visualizzato nell'altro file. Entrambe le ricorrenze sarebbero un diffrisultato.
Volker Siegel,
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.