Prüfen Sie, ob alle Dateizeilen in einer anderen Datei vorkommen

14

Ich habe zwei Dateien: file1 mit ungefähr 10 000 Zeilen und file2 mit ein paar hundert Zeilen. Ich möchte überprüfen, ob alle Zeilen von Datei2 in Datei1 vorkommen. Das heißt: ∀ Zeile ℓ ℓ Datei2: ℓ ℓ Datei1

Sollte jemand nicht wissen, was diese Symbole bedeuten oder was "Prüfen, ob alle Zeilen von Datei2 in Datei1 vorkommen" bedeutet: Mehrere gleichwertige Zeilen in beiden Dateien haben keinen Einfluss darauf, ob die Prüfung ergibt, dass die Dateien die Anforderung erfüllen oder nicht.

Wie mache ich das?

UTF-8
quelle
2
Können diese Dateien doppelte Zeilen haben? Wenn file22 Zeilen enthalten sind A, müssen Sie file1mindestens 2 Zeilen enthalten A?
Stéphane Chazelas
2
@ StéphaneChazelas Alle Zeilen (in beiden Dateien) sind garantiert eindeutig.
UTF-8
1
@ UTF-8 Das wäre ein wichtiges Detail, um deine Frage zu bearbeiten.
David Z
2
@DavidZ Nicht mehr, da die vorhandenen Antworten nicht auf dieser Garantie beruhen. Wenn ich die Frage jetzt bearbeite, würde ich den offensichtlichen Umfang der Antworten reduzieren.
UTF-8
@ UTF-8 Ich nehme an, obwohl die Frage ohne sie etwas mehrdeutig ist, zB wenn eine bestimmte Zeile in Datei2 fünfmal vorkommt, muss diese Zeile in Datei1 auch fünfmal vorkommen (im Gegensatz zu nur einmal)? Wenn Sie diese Anforderung hätten, sieht es nicht so aus, als würde eine der vorhandenen Antworten funktionieren. Daher würde ich vorschlagen, zumindest etwas zu bearbeiten, das deutlich macht, dass dies nicht das ist, was Sie meinen.
David Z

Antworten:

18
comm -13 <(sort -u file_1) <(sort -u file_2)

Dieser Befehl gibt Zeilen aus, die für eindeutig sind file_2. Wenn also die Ausgabe leer ist, sind alle file_2Zeilen im file_1.

Vom Kommunikationsmann:

   With  no  options,  produce  three-column  output.  Column one contains
   lines unique to FILE1, column two contains lines unique to  FILE2,  and
   column three contains lines common to both files.

   -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)
MiniMax
quelle
@don_crissti Stimmt. Behoben: Die -uOption, die dem sortBefehl hinzugefügt wurde . Jetzt bleiben in beiden sortierten Dateien nur einzelne Zeilen übrig.
MiniMax
Unglaublich einfache Lösung! Gilt diese Syntax für alle Programme, die Dateien erwarten? Ich dachte immer, dass das <in stdin geleitet wird. Ändert der Klammerbegriff dies?
UTF-8
2
@ UTF-8 Dies wird als Prozessersetzung bezeichnet . Sie können hier darüber lesen . Und ja, es verhält sich wie eine temporäre Datei, so dass es in jedem Programm, das Dateien erwartet, anstelle von echten Dateien verwendet werden kann.
MiniMax
Wenn Sie dies häufig tun, möchten Sie es möglicherweise file_1in vorsortierter Form speichern . Spart Zeit und Tipparbeit.
Stig Hemmer
7
@minimax Guter Kommentar mit Ausnahme des "any". Die Prozessersetzung ist zwar wunderbar, kann jedoch nicht in allen Fällen verwendet werden, da die resultierenden "Dateien" Streams und keine echten Dateien sind. Dies bedeutet, dass sie nicht wie eine normale Datei "suchbar" sind und nur verwendet werden können, wenn das Programm die Datei von Anfang an normal liest, und nicht, wenn das Programm eine reine Dateifunktionalität verwendet, z. B. das Suchen zu einem bestimmten Punkt oder Zurückspulen, um von vorne zu beginnen. Glücklicherweise lesen die meisten Programme einfach ihre Dateien () und so funktioniert die Prozessersetzung mit den meisten Programmen, aber nicht mit "irgendwelchen" Programmen.
Law29
7
[ $(grep -cxFf file2 <(sort -u file1)) = $(sort -u file2 | wc -l) ] && 
  echo all there || 
  echo some missing

Wenn die Anzahl der Übereinstimmungen von Datei2 in (den eindeutigen Zeilen von) Datei1 mit der Anzahl der eindeutigen Zeilen in Datei2 übereinstimmt, sind alle vorhanden. sonst sind sie nicht.

Jeff Schaller
quelle
5

Verwenden von GNU, awkwo es bestimmte length(array)Funktionen unterstützt (und eine andere awkImplementierung, die dies möglicherweise unterstützt) und nicht erforderlich, wenn Dateien sortiert werden.

gawk 'FNR==NR{seen[$0];next} ($0 in seen){delete seen[$0]};
    END{print (!length(seen))?"Matched":"Not Matched"}' file2 file1

Das liest file2 in ein Array, das seenmit dem Schlüssel als gesamte Zeile von file2 aufgerufen wird .

Lesen Sie dann file1 und löschen Sie diesen Schlüssel für jede Zeile, wenn sie mit den angezeigten Zeilen im Array übereinstimmt.

Wenn das Array am Ende leer war, sind alle Zeilen in Datei2 in Datei1 vorhanden und werden gedruckt Matched, andernfalls wird angezeigt Not Matched.


Für die Kompatibilität in allen awkImplementierungen.

awk 'FNR==NR{seen[$0];next} ($0 in seen){delete seen[$0]};
    END{for(x in seen);print (!x)?"Matched":"Not Matched"}' file2 file1

Wenn Sie leere Zeilen / oder Zeilen mit Leerzeichen nur in Datei2 ignorieren möchten, müssen Sie NFdie Bedingung in hinzufügen, um das NR==FNR && NF {...Einlesen in das Array zu überspringen.

αғsнιη
quelle
length(array)ist nur für Gawk AFAIK; es ist definitiv nicht POSIX.
Dave_thompson_085
@ dave_thompson_085 Richtig, ich habe meine Antwort aktualisiert. danke
αғsнιη
3

Mit commkönnen Sie Linien finden, die in beiden Dateien gemeinsam sind.

comm -12 file1 file2

Schauen Sie sich man commfür weitere Details

Hunter.S.Thompson
quelle
Korrigieren Sie die Rückgabe gemeinsamer Zeilen in beiden Dateien, aber dies liefert keine Antwort auf das Q des OP, wenn Sie eine Zeile in Datei2 hatten, die in Datei1 nicht beendet wird, sodass in Datei1 nicht alle Zeilen von Datei2 vorhanden sind.
αғsнιη
1
Dateien sollten sortiert sein. From man " comm- vergleiche zwei sortierte Dateien Zeile für Zeile".
MiniMax
@MiniMax ist richtig. Das geht nicht. Die andere Antwort commenthält eine Lösung, die offensichtlich nicht falsch ist. Wenn ich Ihren Befehl ausführe, erhalte ich eine Warnung, dass die Dateien nicht in sortierter Reihenfolge sind und viele Zeilen, die sich definitiv in beiden Dateien befinden.
UTF-8
3
diff -q <(sort -u file2) <(grep -Fxf file2 file1 | sort -u)

wird keine Ausgabe erzeugen , wenn file1enthält alle Linien in file2und Ausfahrt mit dem Status 0, drucken Sie es sonst so etwas wie

Files /proc/self/fd/11 and /proc/self/fd/12 differ

und mit Status beenden 1

don_crissti
quelle
2

Verwenden Sie ein Python-Programm:

#!/usr/bin/env python3
import sys

def open_arg(path):
    return sys.stdin if path == '-' else open(path)

def strip_linebreak(s):
    return s[:-1] if s.endswith('\n') else s

with open_arg(sys.argv[1]) as pattern_file:
    patterns = set(map(strip_linebreak, pattern_file))

with open_arg(sys.argv[2]) as dataset_file:
    for l in map(strip_linebreak, dataset_file):
        patterns.remove(l)
        if not patterns:
            break

sys.exit(int(bool(patterns)))

Verwendung:

python3 contains-all.py file2 file1

Der Programm-Exit-Status zeigt an, ob alle Muster von Datei 2 übereinstimmen:

  • 0 (Erfolg) bedeutet, dass alle Muster abgeglichen wurden.
  • 1 (Fehler) bedeutet, dass einige Muster nicht übereinstimmen.

Zur Abfrage des Exit - Status in einer Schale (Skript) Sie entweder die verwenden können $?spezielle Variable oder andere Ausdrücke , den Befehlsbeendigungsstatus, zB Kurzschluss Betreiber evaluieren &&und ||und bedingte Ausdrücke wie ifoder while. Beispiel:

if python3 compare-all.py file2 file1 && some-other --condition; then
    # do stuff
fi
David Foerster
quelle
1

combinefrom moreutils zeigt Ihnen alle Zeilen an file2, die nicht file1mit:

combine file2 not file1

Dann können Sie die Anzahl der Zeilen zählen, indem Sie sie wc -lwie folgt weiterleiten :

if [ $(combine file2 not file1 | wc -l) != 0 ]; then
  echo "lines missing"
else
  echo "You're fine"
fi
Karl Bielefeldt
quelle