Puoi farlo controllando la formattazione delle linee vecchie / nuove / invariate diff
nell'output GNU :
diff --new-line-format="" --unchanged-line-format="" file1 file2
I file di input devono essere ordinati affinché funzioni. Con bash
(e zsh
) è possibile ordinare sul posto con la sostituzione del processo <( )
:
diff --new-line-format="" --unchanged-line-format="" <(sort file1) <(sort file2)
Nelle righe nuove e invariate sopra vengono eliminate , quindi vengono emesse solo le linee modificate (ovvero le linee rimosse nel tuo caso). Si può anche utilizzare un paio di diff
opzioni che altre soluzioni non offrono, come ad esempio -i
di ignorare caso, o le varie opzioni di spaziatura ( -E
, -b
, -v
ecc) per meno severe di corrispondenza.
Spiegazione
Le opzioni --new-line-format
, --old-line-format
e --unchanged-line-format
consentono di controllare il modo in cui diff
formatta le differenze, simili a printf
identificatori di formato. Queste opzioni formattano rispettivamente le linee nuove (aggiunte), vecchie (rimosse) e invariate . L'impostazione di uno su "" impedisce l'output di quel tipo di linea.
Se hai familiarità con il formato diff unificato , puoi ricrearlo parzialmente con:
diff --old-line-format="-%L" --unchanged-line-format=" %L" \
--new-line-format="+%L" file1 file2
Lo %L
specificatore è la riga in questione e ogni prefisso è "+" "-" o "", come diff -u
(si noti che genera solo differenze, manca le righe ---
+++
e @@
nella parte superiore di ogni modifica raggruppata). È inoltre possibile utilizzare questo per fare altre cose utili come numero di ogni riga con %dn
.
Il diff
metodo (insieme ad altri suggerimenti comm
e join
) produce solo l'output previsto con input ordinato , sebbene sia possibile utilizzare <(sort ...)
per ordinare in posizione. Ecco un semplice awk
script (nawk) (ispirato agli script collegati nella risposta di Konsolebox) che accetta file di input ordinati in modo arbitrario e genera le righe mancanti nell'ordine in cui si verificano nel file1.
# output lines in file1 that are not in file2
BEGIN { FS="" } # preserve whitespace
(NR==FNR) { ll1[FNR]=$0; nl1=FNR; } # file1, index by lineno
(NR!=FNR) { ss2[$0]++; } # file2, index by string
END {
for (ll=1; ll<=nl1; ll++) if (!(ll1[ll] in ss2)) print ll1[ll]
}
Ciò memorizza l'intero contenuto di file1 riga per riga in un array indicizzato numero riga ll1[]
e l'intero contenuto di file2 riga per riga in un array associativo indicizzato contenuto riga ss2[]
. Dopo aver letto entrambi i file, scorrere ll1
e utilizzare l' in
operatore per determinare se la riga in file1 è presente in file2. (Questo avrà un output diverso rispetto al diff
metodo se ci sono duplicati.)
Nel caso in cui i file siano sufficientemente grandi da archiviarli entrambi, si verifica un problema di memoria, è possibile scambiare la CPU con la memoria archiviando solo file1 ed eliminando le corrispondenze lungo la lettura di file2.
BEGIN { FS="" }
(NR==FNR) { # file1, index by lineno and string
ll1[FNR]=$0; ss1[$0]=FNR; nl1=FNR;
}
(NR!=FNR) { # file2
if ($0 in ss1) { delete ll1[ss1[$0]]; delete ss1[$0]; }
}
END {
for (ll=1; ll<=nl1; ll++) if (ll in ll1) print ll1[ll]
}
Quanto sopra memorizza l'intero contenuto di file1 in due array, uno indicizzato per numero di riga ll1[]
, uno indicizzato per contenuto di riga ss1[]
. Quindi, quando viene letto file2, ogni riga corrispondente viene eliminata da ll1[]
e ss1[]
. Alla fine vengono emesse le righe rimanenti da file1, preservando l'ordine originale.
In questo caso, con il problema come indicato, puoi anche dividere e conquistare usando GNU split
(il filtro è un'estensione GNU), ripetute esecuzioni con blocchi di file1 e lettura di file2 ogni volta:
split -l 20000 --filter='gawk -f linesnotin.awk - file2' < file1
Nota l'uso e il posizionamento del -
significato stdin
sulla gawk
riga di comando. Questo è fornito da split
file1 in blocchi di 20000 righe per invocazione.
Per gli utenti di sistemi non-GNU, non v'è quasi certamente un coreutils GNU pacchetto è possibile ottenere, anche su OSX come parte delle di Apple Xcode strumenti che fornisce GNU diff
, awk
, anche se solo un POSIX / BSD split
, piuttosto che una versione di GNU.
awk 'NR==FNR{a[$0];next}!($0 in a)' file2 file1 > out.txt