Sie können dies erreichen, indem Sie die Formatierung der alten / neuen / unveränderten Zeilen in der GNU- diff
Ausgabe steuern :
diff --new-line-format="" --unchanged-line-format="" file1 file2
Die Eingabedateien sollten sortiert sein, damit dies funktioniert. Mit bash
(und zsh
) können Sie direkt mit der Prozessersetzung sortieren <( )
:
diff --new-line-format="" --unchanged-line-format="" <(sort file1) <(sort file2)
In den obigen Abschnitten werden neue und unveränderte Zeilen unterdrückt, sodass nur geänderte (in Ihrem Fall entfernte Zeilen) ausgegeben werden. Sie können auch ein paar verwenden diff
Optionen , die anderen Lösungen nicht bieten, wie -i
Fall zu ignorieren, oder verschiedene Leerzeichen Optionen ( -E
, -b
, -v
usw.) für weniger strenges Matching.
Erläuterung
Die Optionen --new-line-format
, --old-line-format
und --unchanged-line-format
lassen Sie die Art und Weise steuern , diff
formatiert die Unterschiede ähnlich printf
Formatbezeichner. Diese Optionen formatieren neue (hinzugefügte), alte (entfernte) bzw. unveränderte Zeilen. Wenn Sie "leer" setzen, wird die Ausgabe dieser Art von Zeile verhindert.
Wenn Sie mit dem einheitlichen Diff- Format vertraut sind , können Sie es teilweise neu erstellen mit:
diff --old-line-format="-%L" --unchanged-line-format=" %L" \
--new-line-format="+%L" file1 file2
Der %L
Bezeichner ist die betreffende Zeile, und wir stellen jeweils "+" "-" oder "" voran diff -u
(beachten Sie, dass nur Unterschiede ausgegeben werden ---
+++
und die @@
Zeilen und oben bei jeder gruppierten Änderung fehlen ). Sie können dies auch verwenden, um andere nützliche Dinge zu tun, z. B. die Nummerierung jeder Zeile mit %dn
.
Die diff
Methode (zusammen mit anderen Vorschlägen comm
und join
) erzeugt nur die erwartete Ausgabe mit sortierter Eingabe, obwohl Sie sie <(sort ...)
zum Sortieren verwenden können. Hier ist ein einfaches awk
(nawk) Skript (inspiriert von den in Konsoleboxs Antwort verknüpften Skripten), das willkürlich geordnete Eingabedateien akzeptiert und die fehlenden Zeilen in der Reihenfolge ausgibt, in der sie in Datei1 vorkommen.
# 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]
}
Dies speichert den gesamten Inhalt von Datei1 Zeile für Zeile in einem Zeilennummern-indizierten Array ll1[]
und den gesamten Inhalt von Datei2 Zeile für Zeile in einem Zeileninhalt-indizierten assoziativen Array ss2[]
. Nachdem beide Dateien gelesen wurden, wiederholen Sie den Vorgang ll1
und verwenden Sie den in
Operator, um festzustellen, ob die Zeile in Datei1 in Datei2 vorhanden ist. (Dies hat eine andere Ausgabe als die diff
Methode, wenn Duplikate vorhanden sind.)
Für den Fall, dass die Dateien so groß sind, dass das Speichern beider Dateien ein Speicherproblem verursacht, können Sie die CPU gegen Speicher tauschen, indem Sie nur Datei1 speichern und Übereinstimmungen während des Lesens von Datei2 löschen.
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]
}
Oben wird der gesamte Inhalt von Datei1 in zwei Arrays gespeichert, von denen eines nach Zeilennummer ll1[]
und eines nach Zeileninhalt indiziert ist ss1[]
. Beim Lesen von Datei2 wird dann jede übereinstimmende Zeile aus ll1[]
und gelöscht ss1[]
. Am Ende werden die verbleibenden Zeilen aus Datei1 ausgegeben, wobei die ursprüngliche Reihenfolge beibehalten wird.
In diesem Fall können Sie mit dem angegebenen Problem auch mithilfe von GNU (Filterung ist eine GNU-Erweiterung) teilen und siegensplit
, wiederholte Läufe mit Blöcken von Datei1 ausführen und Datei2 jedes Mal vollständig lesen:
split -l 20000 --filter='gawk -f linesnotin.awk - file2' < file1
Beachten Sie die Verwendung und Platzierung der -
Bedeutung stdin
in der gawk
Befehlszeile. Dies wird von split
aus Datei1 in Blöcken von 20000 Zeilen pro Aufruf bereitgestellt .
Für Benutzer auf nicht-GNU - Systemen gibt an Sicherheit grenzender Wahrscheinlichkeit ist ein GNU coreutils Paket , das Sie erhalten, auf OSX auch im Rahmen der Apple - Xcode - Tool , das GNU bietet diff
, awk
allerdings nur ein POSIX / BSD split
eher als eine GNU - Version.
awk 'NR==FNR{a[$0];next}!($0 in a)' file2 file1 > out.txt