Suchen Sie die Dateien in einem Verzeichnis, aber nicht im anderen [geschlossen]

295

Ich versuche, die in einem Verzeichnis vorhandenen Dateien zu finden, aber nicht im anderen. Ich habe versucht, diesen Befehl zu verwenden:

diff -q dir1 dir2

Das Problem mit dem obigen Befehl, dass es sowohl die Dateien in dir1aber nicht in dir2als auch die Dateien in dir2aber nicht in findet dir1,

Ich versuche, die Dateien in zu finden, dir1aber nicht dir2nur in.

Hier ist eine kleine Auswahl meiner Daten

dir1    dir2    dir3
1.txt   1.txt   1.txt
2.txt   3.txt   3.txt
5.txt   4.txt   5.txt
6.txt   7.txt   8.txt

Eine andere Frage in meinem Kopf ist, wie ich die Dateien in, dir1aber nicht in dir2oder dir3in einem einzelnen Befehl finden kann.

Fehler 404
quelle

Antworten:

390
diff -r dir1 dir2 | grep dir1 | awk '{print $4}' > difference1.txt

Erläuterung:

  • diff -r dir1 dir2 Zeigt an, welche Dateien sich nur in dir1 und welche nur in dir2 befinden, sowie die Änderungen der Dateien, die in beiden Verzeichnissen vorhanden sind, falls vorhanden.

  • diff -r dir1 dir2 | grep dir1 Zeigt an, welche Dateien sich nur in dir1 befinden

  • awk um nur den Dateinamen zu drucken.

Asclepix
quelle
5
Ich möchte grepfür etw ^dir1sicherstellen, dass ich dir1später auf dem Weg nicht auftauche.
Alfe
@ Alfe Es kann verbessert werden. Ich benutze $4als Beispiel. In der Tat, auf meinem aktuellen Ubuntu, diffantwortet auf Italienisch. $4ist in Ordnung für italienische und englische Antworten, aber ich bin mir nicht sicher für alle anderen Sprachen ...
asclepix
139

Dies sollte den Job machen:

diff -rq dir1 dir2

Optionen erklärt (über die Manpage diff (1) ):

  • -r - Vergleichen Sie alle gefundenen Unterverzeichnisse rekursiv.
  • -q - Nur ausgeben, ob sich Dateien unterscheiden.
tokhi
quelle
8
Nett! Aber ich denke, es sollte so erweitert werden:diff -rq dir1 dir2 | grep 'Only in dir1/'
sobi3ch
2
Dies ist ein Vergleich nach Inhalten, kann jedoch auf langsamen Laufwerken lange dauern.
Smeterlink
5
Nur ein Hinweis zur -qOption: Auf den Manpages wird nur "Nur ausgeben, ob Dateien unterschiedlich sind" angezeigt, nicht wie überprüft wird, ob sie unterschiedlich sind. Ich habe den Quellcode durchgesehen und festgestellt, dass nur die Dateigrößen überprüft werden, um Unterschiede festzustellen, nicht der tatsächliche Inhalt.
Ryancdotnet
In Bezug auf die -qOption kann ich nicht reproduzieren, dass nur die Dateigröße überprüft wird. Verwenden von GNU Diffutils 3.7 zum Vergleichen von zwei Dateien mit derselben Dateigröße, aber unterschiedlichem Inhalt mit diff -q file1 file2Ausgaben Files file1 and file2 differ.
Stefan Schmidt
50
comm -23 <(ls dir1 |sort) <(ls dir2|sort)

Dieser Befehl gibt Ihnen Dateien, die sich in dir1 und nicht in dir2 befinden.

Über <( )sign können Sie es als "Prozessersetzung" googeln.

plhn
quelle
Es wäre in Ordnung, auch mit Unterverzeichnissen zu arbeiten. Ich denke, ich (ls -R dir1|sort)könnte es schaffen
ulkas
1
Dies würde im OS X-Wiederherstellungsmodus funktionieren.
Anthony Vanover
@ulkas, die Ausgabe könnte falsch sein, wenn Sie verwenden (ls -R dir|sort).
Andriy Makukha
3
vimdiff bietet einen viel schöneren visuellen Vergleich mit der Hervorhebung von Farben:vimdiff <(ls dir1 |sort) <(ls dir2|sort)
Logan Reed
32

Ein guter Weg, um diesen Vergleich durchzuführen, ist die Verwendung findmit md5sum, dann a diff.

Beispiel:

Verwenden Sie finddiese Option, um alle Dateien im Verzeichnis aufzulisten. Berechnen Sie dann den MD5-Hash für jede Datei und leiten Sie ihn an eine Datei weiter:

find /dir1/ -type f -exec md5sum {} \; > dir1.txt

Gehen Sie genauso in das andere Verzeichnis vor:

find /dir2/ -type f -exec md5sum {} \; > dir2.txt

Vergleichen Sie dann die beiden Ergebnisdateien mit "diff":

diff dir1.txt dir2.txt

Diese Strategie ist sehr nützlich, wenn sich die beiden zu vergleichenden Verzeichnisse nicht auf demselben Computer befinden und Sie sicherstellen müssen, dass die Dateien in beiden Verzeichnissen gleich sind.

Ein weiterer guter Weg, um die Arbeit zu erledigen, ist die Verwendung von Git

git diff --no-index dir1/ dir2/

Freundliche Grüße!

Adail Junior
quelle
1
Ich bin nicht gegangen, Git könnte einen Unterschied bei beliebigen Verzeichnissen machen, die sich nicht in einem Git-Repo befinden ... großartig !!! Diese Antwort hat gerade ein großes Problem für mich gelöst, danke
ViktorNova
17

Meld ( http://meldmerge.org/ ) leistet hervorragende Arbeit beim Vergleich von Verzeichnissen und den darin enthaltenen Dateien.

Meld vergleicht Verzeichnisse

Catalin Hritcu
quelle
Außer, dass meld einen miesen Job macht, wenn es um Zeilenenden geht ...
0xC0000022L
1
Hatte nie ein Problem mit Zeilenenden. Können Sie detaillieren?
Catalin Hritcu
Ja, die Zeilenenden werden nicht angezeigt . Dies hat (wiederholt) dazu geführt, dass Entwickler, die dieses Tool verwenden, Änderungen vornehmen, die die Zeilenenden "korrigierten", indem sie beispielsweise eine CRLF in CRLFLF umwandelten.
0xC0000022L
3
Es besteht auch darauf, Dateiinhalte zu lesen, und ist daher bei >> 1-GB-Verzeichnissen nahezu unbrauchbar.
Tomislav Nakic-Alfirevic
13

Das DirDiff- Plugin von vim ist ein weiteres sehr nützliches Tool zum Vergleichen von Verzeichnissen.

vim -c "DirDiff dir1 dir2"

Es werden nicht nur die Dateien aufgelistet, die sich zwischen den Verzeichnissen unterscheiden, sondern Sie können auch die unterschiedlichen Dateien mit vimdiff überprüfen / ändern.

Drrossum
quelle
11

Unzufrieden mit allen Antworten, da die meisten sehr langsam arbeiten und unnötig lange Ausgaben für große Verzeichnisse erzeugen, habe ich mein eigenes Python-Skript geschrieben, um zwei Ordner zu vergleichen.

Im Gegensatz zu vielen anderen Lösungen werden die Inhalte der Dateien nicht verglichen. Es geht auch nicht in Unterverzeichnisse, die in einem anderen Verzeichnis fehlen. Die Ausgabe ist also sehr präzise und das Skript funktioniert schnell.

#!/usr/bin/env python3

import os, sys

def compare_dirs(d1: "old directory name", d2: "new directory name"):
    def print_local(a, msg):
        print('DIR ' if a[2] else 'FILE', a[1], msg)
    # ensure validity
    for d in [d1,d2]:
        if not os.path.isdir(d):
            raise ValueError("not a directory: " + d)
    # get relative path
    l1 = [(x,os.path.join(d1,x)) for x in os.listdir(d1)]
    l2 = [(x,os.path.join(d2,x)) for x in os.listdir(d2)]
    # determine type: directory or file?
    l1 = sorted([(x,y,os.path.isdir(y)) for x,y in l1])
    l2 = sorted([(x,y,os.path.isdir(y)) for x,y in l2])
    i1 = i2 = 0
    common_dirs = []
    while i1<len(l1) and i2<len(l2):
        if l1[i1][0] == l2[i2][0]:      # same name
            if l1[i1][2] == l2[i2][2]:  # same type
                if l1[i1][2]:           # remember this folder for recursion
                    common_dirs.append((l1[i1][1], l2[i2][1]))
            else:
                print_local(l1[i1],'type changed')
            i1 += 1
            i2 += 1
        elif l1[i1][0]<l2[i2][0]:
            print_local(l1[i1],'removed')
            i1 += 1
        elif l1[i1][0]>l2[i2][0]:
            print_local(l2[i2],'added')
            i2 += 1
    while i1<len(l1):
        print_local(l1[i1],'removed')
        i1 += 1
    while i2<len(l2):
        print_local(l2[i2],'added')
        i2 += 1
    # compare subfolders recursively
    for sd1,sd2 in common_dirs:
        compare_dirs(sd1, sd2)

if __name__=="__main__":
    compare_dirs(sys.argv[1], sys.argv[2])

Beispielnutzung:

user@laptop:~$ python3 compare_dirs.py dir1/ dir2/
DIR  dir1/out/flavor-domino removed
DIR  dir2/out/flavor-maxim2 added
DIR  dir1/target/vendor/flavor-domino removed
DIR  dir2/target/vendor/flavor-maxim2 added
FILE dir1/tmp/.kconfig-flavor_domino removed
FILE dir2/tmp/.kconfig-flavor_maxim2 added
DIR  dir2/tools/tools/LiveSuit_For_Linux64 added

Oder wenn Sie nur Dateien aus dem ersten Verzeichnis sehen möchten:

user@laptop:~$ python3 compare_dirs.py dir2/ dir1/ | grep dir1
DIR  dir1/out/flavor-domino added
DIR  dir1/target/vendor/flavor-domino added
FILE dir1/tmp/.kconfig-flavor_domino added

PS Wenn Sie Dateigrößen und Datei-Hashes auf mögliche Änderungen vergleichen müssen, habe ich hier ein aktualisiertes Skript veröffentlicht: https://gist.github.com/amakukha/f489cbde2afd32817f8e866cf4abe779

Andriy Makukha
quelle
Einfach genug Skript, das genau das tut, was ich wollte: Überprüfen Sie eine Massenkopie: +1 von mir. (muss jedoch in python2 konvertiert werden) Hinweis: Die Verwendung von Sets kann den Diff-Teil vereinfachen.
Jason Morgan
6

Ein anderer Ansatz (möglicherweise schneller für große Verzeichnisse):

$ find dir1 | sed 's,^[^/]*/,,' | sort > dir1.txt && find dir2 | sed 's,^[^/]*/,,' | sort > dir2.txt
$ diff dir1.txt dir2.txt

Der sedBefehl entfernt die erste Verzeichniskomponente dank Erik`s Post )

jaltek
quelle
1
Ich glaube, diese Methode ist einfacher ( finddaher wird immer noch ein Kommentar und keine separate Antwort verwendet): cd dir2; find . -exec [ -e ../dir1/{} ] \; -o -print 2>/dev/null Dadurch werden Dateien gedruckt, die in dir2, aber nicht in dir1 vorhanden sind.
Alexander Amelkin
5

Dies ist etwas spät, kann aber jemandem helfen. Ich bin mir nicht sicher, ob diff oder rsync nur Dateinamen in einem bloßen Format wie diesem ausspucken. Vielen Dank an plhn für diese nette Lösung, die ich weiter unten erweitert habe.

Wenn Sie nur die Dateinamen verwenden möchten, damit Sie die benötigten Dateien einfach in ein sauberes Format kopieren können, können Sie den Befehl find verwenden.

comm -23 <(find dir1 | sed 's/dir1/\//'| sort) <(find dir2 | sed 's/dir2/\//'| sort) | sed 's/^\//dir1/'

Dies setzt voraus, dass sich sowohl dir1 als auch dir2 im selben übergeordneten Ordner befinden. sed entfernt nur den übergeordneten Ordner, damit Sie Äpfel mit Äpfeln vergleichen können. Das letzte Sed setzt nur den Namen dir1 zurück.

Wenn Sie nur Dateien möchten:

comm -23 <(find dir1 -type f | sed 's/dir1/\//'| sort) <(find dir2 -type f | sed 's/dir2/\//'| sort) | sed 's/^\//dir1/'

Ähnliches gilt für Verzeichnisse:

comm -23 <(find dir1 -type d | sed 's/dir1/\//'| sort) <(find dir2 -type d | sed 's/dir2/\//'| sort) | sed 's/^\//dir1/'
James
quelle
1
Beachten Sie, dass Sie eine cdvor dem findausführen können, anstatt sie verwenden zu müssen sed, z comm -23 <(cd dir1 || exit; find -type f | sort) <(cd dir2 || exit; find -type f | sort). (Die exits sind hier, um zu verhindern, dass finddas aktuelle Verzeichnis verwendet wird, sollte cdfehlschlagen.)
phk
Beachten Sie auch, dass Ihre Lösung möglicherweise fehlschlägt, wenn Dateien mit bestimmten Sonderzeichen vorhanden sind. Wenn Sie eine neuere Version commmit Unterstützung haben -z(mit git.savannah.gnu.org/cgit/coreutils.git/commit/… geliefert ), können Sie dies tun comm -23 -z <(cd dir1 && find -type f -print0 | sort -z) <(cd dir2 && find -type f -print0 | sort -z). (In der Zwischenzeit habe ich auch herausgefunden, dass das exits ersetzt werden könnte.)
phk
5

In der akzeptierten Antwort werden auch die Dateien aufgelistet, die in beiden Verzeichnissen vorhanden sind, jedoch unterschiedliche Inhalte haben. Um NUR die in dir1 vorhandenen Dateien aufzulisten, können Sie Folgendes verwenden:

diff -r dir1 dir2 | grep 'Only in' | grep dir1 | awk '{print $4}' > difference1.txt

Erläuterung:

  • diff -r dir1 dir2: vergleiche
  • grep 'Nur in': Ruft Zeilen ab, die 'Nur in' enthalten.
  • grep dir1: Ruft Zeilen ab, die dir enthalten
Aris
quelle
5

Diese Antwort optimiert einen der Vorschläge von @ Adail-Junior durch Hinzufügen des -D Option. ist hilfreich, wenn keines der verglichenen Verzeichnisse Git-Repositorys sind:

git diff -D --no-index dir1/ dir2/

Wenn Sie verwenden -D, werden keine Vergleiche angezeigt mit /dev/null: text Binary files a/whatever and /dev/null differ

Mike Slinn
quelle
War sehr nützlich beim Vergleich zweier Verzeichnisse, sieht man sofort die Unterschiede zwischen den Dateien. Natürlich funktioniert es am besten bei Dateien mit Textinhalt.
Erich Kuester
1

Eine vereinfachte Methode zum Vergleichen von 2 Verzeichnissen mit dem Befehl DIFF

diff filename.1 filename.2> filename.dat >> Enter

Öffnen Sie filename.dat, nachdem der Lauf abgeschlossen ist

und Sie werden sehen: Nur in Dateiname.1: Dateiname.2 Nur in: Verzeichnisname: Name_der_Datei1 Nur in: Verzeichnis_Name: Name_der_Datei2

Nerakk
quelle
Warum müssen Sie in eine .dat-Datei ausgeben?
Vishnu NK
1

Dies ist das Bash-Skript zum Drucken von Befehlen zum Synchronisieren von zwei Verzeichnissen

dir1=/tmp/path_to_dir1
dir2=/tmp/path_to_dir2
diff -rq $dir1 $dir2 | sed -e "s|Only in $dir2\(.*\): \(.*\)|cp -r $dir2\1/\2 $dir1\1|" |  sed -e "s|Only in $dir1\(.*\): \(.*\)|cp -r $dir1\1/\2 $dir2\1|" 
Ebru Yener
quelle
0

GNU grepkann die Suche mit der Option umkehren -v. Dadurch werden grepdie Zeilen gemeldet, die nicht übereinstimmen. Auf diese Weise können Sie die Dateien in dir2aus der Liste der Dateien in entfernen dir1.

grep -v -F -x -f <(find dir2 -type f -printf '%P\n') <(find dir1 -type f -printf '%P\n')

Die Optionen -F -xweisen grepan, eine Zeichenfolgensuche für die gesamte Zeile durchzuführen.

ceving
quelle