Esiste uno strumento in grado di ottenere righe che contiene il file A, ma il file B no? Potrei fare un piccolo script semplice con, ad esempio, perl, ma se qualcosa del genere esiste già, d'ora in poi risparmierò tempo.
Esiste uno strumento in grado di ottenere righe che contiene il file A, ma il file B no? Potrei fare un piccolo script semplice con, ad esempio, perl, ma se qualcosa del genere esiste già, d'ora in poi risparmierò tempo.
Risposte:
Sì. Lo grep
strumento 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
-u
argomento 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.
diff
pipeline funziona a meraviglia grazie.
grep
necessario. Esempio:grep -F -x -v -f <(sort fileB) <(sort fileA)
diff
è che la posizione nel file viene presa in considerazione.
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 comm
potrebbe eseguire il filtro che stai cercando. Fa parte dei coreutils.
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 b
che non sono nel file a
:
$ comm <(sort a) <(sort b) -3
6
comm
; sfortunatamente, comm
richiede file ordinati
<()
? Funziona e lo capisco, ma c'è un nome per questa stranezza?
<()
è anche noto come sostituzione del processo .
comm
fu 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.
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
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
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