Vergleichen von zwei Dateien im Linux-Terminal

168

Es gibt zwei Dateien mit den Namen "a.txt" und "b.txt", die beide eine Liste von Wörtern enthalten. Jetzt möchte ich überprüfen, welche Wörter in "a.txt" extra sind und welche nicht in "b.txt" .

Ich brauche einen effizienten Algorithmus, da ich zwei Wörterbücher vergleichen muss.

Ali Imran
quelle
27
diff a.txt b.txtist nicht genug?
ThanksForAllTheFish
Können die Wörter in jeder Datei mehrmals vorkommen? Können Sie die Dateien sortieren?
Basile Starynkevitch
Ich brauche nur die Wörter, die nicht in "b.txt" vorhanden sind und in a.txt vorhanden sind
Ali Imran

Antworten:

343

Wenn Sie vim installiert haben, versuchen Sie Folgendes:

vimdiff file1 file2

oder

vim -d file1 file2

Sie werden es fantastisch finden.Geben Sie hier die Bildbeschreibung ein

Fengya Li
quelle
9
Auf jeden Fall fantastisch, gut im Design und leicht herauszufinden, welche Unterschiede es gibt. Ohmygod
Zen
1
Ihre Antwort ist fantastisch, aber mein Lehrer forderte mich auf, keine Bibliotheksfunktion zu verwenden: P
Ali Imran
1
Was für ein tolles Werkzeug! Das ist sehr hilfreich.
user1205577
1
Was bedeuten diese Farben?
Zygimantus
1
Die farbigen Codes bedeuten, dass sie sich in zwei Dateien unterscheiden. @zygimantus
Fengya Li
73

Sortieren Sie sie und verwenden Sie comm:

comm -23 <(sort a.txt) <(sort b.txt)

commvergleicht (sortierte) Eingabedateien und gibt standardmäßig drei Spalten aus: Zeilen, die für a eindeutig sind, Zeilen, die für b eindeutig sind, und Zeilen, die in beiden vorhanden sind. Durch die Angabe -1, -2und / oder -3Sie können die entsprechende Ausgabe zu unterdrücken. comm -23 a bListet daher nur die Einträge auf, die für a eindeutig sind. Ich benutze die <(...)Syntax, um die Dateien im laufenden Betrieb zu sortieren. Wenn sie bereits sortiert sind, brauchen Sie diese nicht.

Anders Johansson
quelle
Ich habe meine eigene Antwort nur mit grep-Befehlen hinzugefügt. Bitte sagen Sie mir, ob sie effizienter ist.
Ali Imran
3
@AliImran commist effizienter, da es die Arbeit in einem einzigen Lauf erledigt, ohne die gesamte Datei im Speicher zu speichern. Da Sie Wörterbücher verwenden, die höchstwahrscheinlich bereits sortiert sind, brauchen Sie sie nicht einmal sort. Wenn Sie grep -f file1 file2dagegen verwenden, wird der gesamte file1Speicher in den Speicher geladen und jede Zeile file2mit all diesen Einträgen verglichen , was viel weniger effizient ist. Es ist meistens nützlich für kleine, unsortierte -f file1.
Anders Johansson
1
Vielen Dank an @AndersJohansson für die Freigabe des Befehls "comm". Es ist in der Tat geschickt. Ich muss häufig äußere Verknüpfungen zwischen Dateien durchführen, und das macht den Trick.
Blispr
Achten Sie auf das neue Linienzeichen ... Ich habe gerade festgestellt, dass \nes auch zum Vergleichen enthalten sein wird.
Bin
31

Try sdiff( man sdiff)

sdiff -s file1 file2
Mudrii
quelle
28

Sie können das diffTool unter Linux verwenden, um zwei Dateien zu vergleichen. Sie können die Optionen --changed-group-format und --unchanged-group-format verwenden , um die erforderlichen Daten zu filtern.

Die folgenden drei Optionen können verwendet werden, um die relevante Gruppe für jede Option auszuwählen:

  • '% <' ruft Zeilen aus FILE1 ab

  • '%>' ruft Zeilen aus FILE2 ab

  • '' (leere Zeichenfolge) zum Entfernen von Zeilen aus beiden Dateien.

Beispiel: diff --changed-group-format = "% <" --unchanged-group-format = "" file1.txt file2.txt

[root@vmoracle11 tmp]# cat file1.txt 
test one
test two
test three
test four
test eight
[root@vmoracle11 tmp]# cat file2.txt 
test one
test three
test nine
[root@vmoracle11 tmp]# diff --changed-group-format='%<' --unchanged-group-format='' file1.txt file2.txt 
test two
test four
test eight
Manjula
quelle
27

Wenn Sie den Diff-Ausgabestil bevorzugen git diff, können Sie ihn mit dem --no-indexFlag verwenden, um Dateien zu vergleichen, die sich nicht in einem Git-Repository befinden:

git diff --no-index a.txt b.txt

Unter Verwendung einiger Dateien mit jeweils etwa 200.000 Dateinamenzeichenfolgen habe ich timediesen Ansatz (mit dem integrierten Befehl) mit einigen der anderen Antworten hier verglichen :

git diff --no-index a.txt b.txt
# ~1.2s

comm -23 <(sort a.txt) <(sort b.txt)
# ~0.2s

diff a.txt b.txt
# ~2.6s

sdiff a.txt b.txt
# ~2.7s

vimdiff a.txt b.txt
# ~3.2s

commscheint bei weitem der schnellste zu sein, während git diff --no-indexer der schnellste Ansatz für die Ausgabe im Diff-Stil zu sein scheint.


Update 2018-03-25 Sie können das --no-indexFlag tatsächlich weglassen, es sei denn, Sie befinden sich in einem Git-Repository und möchten nicht verfolgte Dateien in diesem Repository vergleichen. Aus den Manpages :

In diesem Formular werden die beiden angegebenen Pfade im Dateisystem verglichen. Sie können die Option --no-index weglassen, wenn Sie den Befehl in einem von Git gesteuerten Arbeitsbaum ausführen und mindestens einer der Pfade außerhalb des Arbeitsbaums zeigt oder wenn Sie den Befehl außerhalb eines von Git gesteuerten Arbeitsbaums ausführen.

joelostblom
quelle
4

Verwendung comm -13 (erfordert sortierte Dateien) :

$ cat file1
one
two
three

$ cat file2
one
two
three
four

$ comm -13 <(sort file1) <(sort file2)
four
Chris Seymour
quelle
1

Hier ist meine Lösung dafür:

mkdir temp
mkdir results
cp /usr/share/dict/american-english ~/temp/american-english-dictionary
cp /usr/share/dict/british-english ~/temp/british-english-dictionary
cat ~/temp/american-english-dictionary | wc -l > ~/results/count-american-english-dictionary
cat ~/temp/british-english-dictionary | wc -l > ~/results/count-british-english-dictionary
grep -Fxf ~/temp/american-english-dictionary ~/temp/british-english-dictionary > ~/results/common-english
grep -Fxvf ~/results/common-english ~/temp/american-english-dictionary > ~/results/unique-american-english
grep -Fxvf ~/results/common-english ~/temp/british-english-dictionary > ~/results/unique-british-english
Ali Imran
quelle
2
Haben Sie eine der anderen Lösungen ausprobiert? War eine dieser Lösungen für Sie nützlich? Ihre Frage ist allgemein genug, um viele Benutzer anzulocken, aber Ihre Antwort ist spezifischer für meinen Geschmack ... Für meinen speziellen Fall sdiff -s file1 file2war dies nützlich.
Metafaniel
@ Metafaniel meine Lösung verwenden nicht den Befehl sdiff. Es werden nur in Linux integrierte Befehle verwendet, um das Problem zu lösen.
Ali Imran
-1

Verwenden Sie awk dafür. Testdateien:

$ cat a.txt
one
two
three
four
four
$ cat b.txt
three
two
one

Die awk:

$ awk '
NR==FNR {                    # process b.txt  or the first file
    seen[$0]                 # hash words to hash seen
    next                     # next word in b.txt
}                            # process a.txt  or all files after the first
!($0 in seen)' b.txt a.txt   # if word is not hashed to seen, output it

Duplikate werden ausgegeben:

four
four

Um Duplikate zu vermeiden, fügen Sie jedes neu getroffene Wort in a.txt zu seenHash hinzu:

$ awk '
NR==FNR {
    seen[$0]
    next
}
!($0 in seen) {              # if word is not hashed to seen
    seen[$0]                 # hash unseen a.txt words to seen to avoid duplicates 
    print                    # and output it
}' b.txt a.txt

Ausgabe:

four

Wenn die Wortlisten durch Kommas getrennt sind, wie:

$ cat a.txt
four,four,three,three,two,one
five,six
$ cat b.txt
one,two,three

Sie müssen ein paar zusätzliche Runden ( forLoops) fahren:

awk -F, '                    # comma-separated input
NR==FNR {
    for(i=1;i<=NF;i++)       # loop all comma-separated fields
        seen[$i]
    next
}
{
    for(i=1;i<=NF;i++)
        if(!($i in seen)) {
             seen[$i]        # this time we buffer output (below):
             buffer=buffer (buffer==""?"":",") $i
        }
    if(buffer!="") {         # output unempty buffers after each record in a.txt
        print buffer
        buffer=""
    }
}' b.txt a.txt

Diesmal ausgeben:

four
five,six
James Brown
quelle