Dies ist möglich, indem die Formatierung der alten/neuen/unchanged Zeilen im GNU diff
Output kontrolliert wird:
diff --new-line-format="" --unchanged-line-format="" file1 file2
Die Eingabedateien müssen sortiert sein, damit dies funktioniert. Mit bash
(und zsh
) kann man mit Prozess-Substitution <( )
direkt sortieren:
diff --new-line-format="" --unchanged-line-format="" <(sort file1) <(sort file2)
In den obigen neuen und unchanged Zeilen werden unterdrückt, so dass nur geänderte (d.h. entfernte Zeilen in Ihrem Fall) ausgegeben werden. Sie können auch ein paar diff
Optionen nutzen, die von anderen Lösungen nicht angeboten werden, wie z.B. -i
um Groß- und Kleinschreibung zu ignorieren, oder verschiedene Whitespace-Optionen (-E
, -b
, -v
etc) für weniger striktes Matching.
Erklärung
Die Optionen --new-line-format
, --old-line-format
und --unchanged-line-format
ermöglichen es, die Art und Weise zu kontrollieren, wie diff
die Unterschiede formatiert, ähnlich wie bei printf
Format-Spezifiern. Diese Optionen formatieren jeweils neue (hinzugefügte), alte (entfernte) und unchanged Zeilen. Durch das Setzen auf leer "" wird die Ausgabe dieser Art von Zeile verhindert.
Falls Sie mit dem vereinigten Diff Format vertraut sind, können Sie es teilweise mit folgendem Befehl rekonstruieren:
diff --old-line-format="-%L" --unchanged-line-format=" %L" \
--new-line-format="+%L" file1 file2
Der %L
Spezifizierer ist die Zeile in Frage, und wir ergänzen jedes mit "+" "-" oder " ", wie bei diff -u
(beachten Sie, dass es nur Unterschiede ausgibt, es fehlen die ---
, +++
und @@
Zeilen oben bei jeder zusammengefassten Änderung). Damit können Sie auch andere nützliche Dinge tun, wie zum Beispiel jede Zeile nummerieren mit %dn
.
Die diff
Methode (zusammen mit anderen Vorschlägen comm
und join
) gibt nur die erwartete Ausgabe mit sortierten Eingaben, obwohl Sie <(sort ...)
verwenden können um direkt zu sortieren. Hier ist ein einfaches awk
(nawk) Skript (inspiriert von den Skripten in Konsolebox's Antwort), das beliebig sortierte Eingabedateien akzeptiert, und die fehlenden Zeilen in der Reihenfolge ausgibt, in der sie in file1 auftreten.
# gib Zeilen in file1 aus, die nicht in file2 sind
BEGIN { FS="" } # Erhalte Whitespace
(NR==FNR) { ll1[FNR]=$0; nl1=FNR; } # Datei1, indiziert nach Zeilennummer
(NR!=FNR) { ss2[$0]++; } # Datei2, indiziert nach String
END {
for (ll=1; ll<=nl1; ll++) if (!(ll1[ll] in ss2)) print ll1[ll]
}
Dieses Skript speichert die gesamten Inhalte von file1 zeilenweise in einem Array mit Index der Zeilennummer ll1[]
, und die gesamten Inhalte von file2 zeilenweise in einem assoziativen Array indiziert nach Zeileninhalt ss2[]
. Nachdem beide Dateien eingelesen sind, wird über ll1
iteriert und der in
Operator verwendet, um festzustellen, ob die Zeile in file1 in file2 vorhanden ist. (Dies führt zu einem anderen Ergebnis als die diff
Methode, wenn es Duplikate gibt.)
Falls die Dateien groß genug sind, dass das Speichern beider Dateien ein Speicherproblem verursacht, können Sie CPU gegen Speicher handeln, indem Sie nur file1 speichern und übereinstimmungen löschen, während file2 eingelesen wird.
BEGIN { FS="" }
(NR==FNR) { # Datei1, indiziert nach Zeilennummer und String
ll1[FNR]=$0; ss1[$0]=FNR; nl1=FNR;
}
(NR!=FNR) { # Datei2
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]
}
Obiges speichert die gesamten Inhalte von file1 in zwei Arrays, eins indiziert nach Zeilennummer ll1[]
, eins indiziert nach Zeileninhalt ss1[]
. Dann werden beim Einlesen von file2 die übereinstimmenden Zeilen aus ll1[]
und ss1[]
gelöscht. Am Ende werden die verbleibenden Zeilen aus file1 ausgegeben, wobei die ursprüngliche Reihenfolge beibehalten wird.
In diesem Fall, mit dem genannten Problem, können Sie auch teilen und erobern mit GNU split
(Filterung ist eine GNU-Erweiterung), wiederholten Durchläufen mit Dateichunks von file1 und jedes Mal file2 komplett einlesen:
split -l 20000 --filter='gawk -f linesnotin.awk - file2' < file1
Beachten Sie die Verwendung und Platzierung von -
was auf stdin
auf der gawk
Befehlszeile hinweist. Dies wird von split
von file1 in Chunks von 20000 Zeilen pro Aufruf bereitgestellt.
Für Benutzer auf nicht-GNU-Systemen gibt es fast sicher ein GNU Coreutils-Paket, das Sie erhalten können, einschließlich auf OSX als Teil der Apple Xcode Tools, die GNU diff
und awk
bereitstellen, jedoch nur eine POSIX/BSD split
Version anstelle einer GNU Version.