Znajdź pliki istniejące w jednym katalogu, ale nie w drugim [zamknięte]


295

Próbuję znaleźć pliki istniejące w jednym katalogu, ale nie w drugim, próbowałem użyć tego polecenia:

diff -q dir1 dir2

Problem z powyższym poleceniem polegającym na tym, że znajduje on zarówno pliki w, dir1ale nie w, dir2jak również pliki w, dir2ale nie w dir1,

Próbuję znaleźć pliki, dir1ale nie dir2tylko.

Oto mała próbka tego, jak wyglądają moje dane

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

Kolejne pytanie, jakie mam na myśli, to w jaki sposób mogę znaleźć pliki w jednym poleceniu, dir1ale nie w nim dir2lub dir3w jednym poleceniu?

Odpowiedzi:


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

Wyjaśnienie:

  • diff -r dir1 dir2 pokazuje, które pliki są tylko w katalogu 1, a tylko w katalogu 2, a także zmiany plików obecnych w obu katalogach, jeśli takie istnieją.

  • diff -r dir1 dir2 | grep dir1 pokazuje, które pliki znajdują się tylko w katalogu 1

  • awk aby wydrukować tylko nazwę pliku.


5
Ja bym grepdo czegoś jak ^dir1upewnić się, że nie dostać dir1pojawiające się później w ścieżce.
Alfe

@Alfe Można to poprawić. Używam $4jako przykładu. W rzeczywistości na moim rzeczywistym Ubuntu diffodpowiada w języku włoskim. $4jest w porządku dla odpowiedzi w języku włoskim i angielskim, ale nie jestem pewien co do wszystkich innych języków ...
asclepix

139

To powinno wykonać zadanie:

diff -rq dir1 dir2

Opcje wyjaśnione (przez diff (1) strony man ):

  • -r - Rekurencyjnie porównaj wszystkie znalezione podkatalogi.
  • -q - Wyprowadzaj tylko, czy pliki się różnią.

8
Miły! Ale myślę, że powinien zostać przedłużony w ten sposób:diff -rq dir1 dir2 | grep 'Only in dir1/'
sobi3ch

2
Jest to porównanie pod względem zawartości, ale na wolnych dyskach może zająć dużo czasu.
Smeterlink,

5
Tylko uwaga na temat -qopcji: Strony podręcznika mówią tylko „Wypisuj tylko, czy pliki się różnią”, a nie to, jak sprawdza, czy są one różne. Przejrzałem kod źródłowy i odkryłem, że sprawdza on tylko rozmiary plików w celu ustalenia różnic, a nie rzeczywistej zawartości.
ryancdotnet

Jeśli chodzi o -qopcję, nie mogę odtworzyć, że sprawdza ona tylko rozmiar pliku. Używanie GNU Diffutils 3.7 porównując dwa pliki o tym samym rozmiarze, ale z inną zawartością z danymi diff -q file1 file2wyjściowymi Files file1 and file2 differ.
Stefan Schmidt

50
comm -23 <(ls dir1 |sort) <(ls dir2|sort)

To polecenie da ci pliki, które znajdują się w katalogu 1, a nie w katalogu 2.

O <( )znaku możesz google jako „proces podstawienia”.


byłoby dobrze pracować także z podkatalogami, myślę, że (ls -R dir1|sort)może załatwić
sprawę

1
To działałoby w trybie odzyskiwania OS X.
Anthony Vanover,

@ulkas, dane wyjściowe mogą być niepoprawne, jeśli używasz (ls -R dir|sort).
Andriy Makukha

3
vimdiff zapewnia znacznie ładniejsze porównanie wizualne z wyróżnianiem kolorów:vimdiff <(ls dir1 |sort) <(ls dir2|sort)
Logan Reed

32

Dobrym sposobem na dokonanie tego porównania jest użycie findz md5sum, a następnie a diff.

Przykład:

Użyj, findaby wyświetlić listę wszystkich plików w katalogu, a następnie obliczyć skrót md5 dla każdego pliku i potokować go do pliku:

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

Wykonaj tę samą procedurę w innym katalogu:

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

Następnie porównaj wynik dwóch plików z „diff”:

diff dir1.txt dir2.txt

Ta strategia jest bardzo przydatna, gdy dwa katalogi, które mają być porównywane, nie znajdują się na tej samej maszynie i musisz upewnić się, że pliki są równe w obu katalogach.

Innym dobrym sposobem na wykonanie tej pracy jest użycie git

git diff --no-index dir1/ dir2/

Z poważaniem!


1
Nie poszedłem git mógł zrobić różnicę w dowolnych katalogach, które nie znajdują się w repozytorium git ... super !!! Ta odpowiedź właśnie rozwiązała dla mnie duży problem, dziękuję
ViktorNova,

17

Meld ( http://meldmerge.org/ ) świetnie sobie radzi w porównywaniu katalogów i plików w nich zawartych.

Połącz porównywanie katalogów


Tyle że meld ma kiepską robotę, jeśli chodzi o zakończenia linii ...
0xC0000022L

1
Nigdy nie miałem problemu z zakończeniami linii. Czy potrafisz szczegółowo?
Catalin Hritcu,

Tak, nie oznacza to końca linii. Doprowadziło to (wielokrotnie) do tego, że programiści używali tego narzędzia, dokonując zmian, które „naprawiły” zakończenia linii, na przykład przekształcając CRLF w CRLFLF.
0xC0000022L

3
Nalega także na czytanie zawartości plików, dlatego jest prawie bezużyteczny w przypadku katalogów >> 1GB.
Tomislav Nakic-Alfirevic

13

Wtyczka DirDiff vima to kolejne bardzo przydatne narzędzie do porównywania katalogów.

vim -c "DirDiff dir1 dir2"

Nie tylko wyświetla listę plików, które różnią się między katalogami, ale pozwala także na inspekcję / modyfikację za pomocą vimdiff różnych plików.


11

Niezadowolony ze wszystkich odpowiedzi, ponieważ większość z nich działa bardzo wolno i generuje niepotrzebnie długie dane wyjściowe dla dużych katalogów, napisałem własny skrypt Pythona, aby porównać dwa foldery.

W przeciwieństwie do wielu innych rozwiązań, nie porównuje zawartości plików. Nie wchodzi też do podkatalogów, których brakuje w innym katalogu. Wynik jest więc dość zwięzły, a skrypt działa szybko.

#!/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])

Przykładowe użycie:

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

Lub jeśli chcesz zobaczyć tylko pliki z pierwszego katalogu:

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 Jeśli chcesz porównać rozmiary plików i skróty plików pod kątem potencjalnych zmian, opublikowałem zaktualizowany skrypt tutaj: https://gist.github.com/amakukha/f489cbde2afd32817f8e866cf4abe779


Prosty skrypt, który robi dokładnie to, co chciałem: Sprawdź kopię zbiorczą: +1 ode mnie. (należy jednak przekonwertować na python2) Wskazówka: użycie zestawów może uprościć część diff.
Jason Morgan

6

Inne podejście (być może szybsze w przypadku dużych katalogów):

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

sedPolecenie usuwa pierwszy składnik katalog dzięki Erik`s postu )


1
Wierzę, że ta metoda jest prostsza (nadal używa findstąd komentarza, a nie oddzielnej odpowiedzi): cd dir2; find . -exec [ -e ../dir1/{} ] \; -o -print 2>/dev/null Spowoduje to wydrukowanie plików obecnych w katalogu 2, ale nie obecnych w katalogu 1.
Alexander Amelkin

5

Jest trochę późno, ale może komuś pomóc. Nie jestem pewien, czy diff lub rsync wyrzucają tylko nazwy plików w takim formacie. Dzięki plhn za udostępnienie tego fajnego rozwiązania, które rozwinąłem poniżej.

Jeśli chcesz tylko nazwy plików, aby łatwo było skopiować potrzebne pliki w czystym formacie, możesz użyć polecenia find.

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

Zakłada się, że zarówno katalog 1, jak i katalog 2 znajdują się w tym samym folderze nadrzędnym. sed po prostu usuwa folder nadrzędny, aby można było porównać jabłka z jabłkami. Ostatni sed po prostu przywraca nazwę katalogu 1.

Jeśli chcesz tylko pliki:

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

Podobnie w przypadku katalogów:

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

1
Zauważ, że możesz zrobić coś cdprzed, findzamiast korzystać sednp comm -23 <(cd dir1 || exit; find -type f | sort) <(cd dir2 || exit; find -type f | sort). : . ( exitS są tutaj, aby uniemożliwić findkorzystanie z bieżącego katalogu powinno się cdnie powieść.)
phk

Pamiętaj również, że twoje rozwiązanie może zawieść, gdy obecne są pliki z określonymi znakami specjalnymi, jeśli masz najnowszą wersję commz obsługą -z(dostarczoną z git.savannah.gnu.org/cgit/coreutils.git/commit/… ), co możesz zrobić comm -23 -z <(cd dir1 && find -type f -print0 | sort -z) <(cd dir2 && find -type f -print0 | sort -z). (W międzyczasie też zorientowałem się, że exitmożna je wymienić.)
phk

5

Akceptowana odpowiedź wyświetli również listę plików, które istnieją w obu katalogach, ale mają inną zawartość. Aby wyświetlić WYŁĄCZNIE pliki istniejące w katalogu 1, możesz użyć:

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

Wyjaśnienie:

  • diff -r dir1 dir2: porównaj
  • grep „Only in”: pobierz wiersze zawierające „Only in”
  • grep dir1: pobierz linie zawierające katalog

5

Ta odpowiedź optymalizuje jedną z sugestii @ Adail-Junior, dodając -Dopcję, która jest przydatna, gdy żaden z porównywanych katalogów nie jest repozytoriami git:

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

Jeśli użyjesz -D, nie zobaczysz porównań z /dev/null: text Binary files a/whatever and /dev/null differ


Był bardzo przydatny w porównywaniu dwóch katalogów, widać natychmiast różnice między plikami. Oczywiście najlepiej działa na plikach z zawartością tekstową.
Erich Kuester

1

Uproszczony sposób porównywania 2 katalogów za pomocą polecenia DIFF

diff nazwa_pliku.1 nazwa_pliku.2> nazwa_pliku.dat >> Enter

otwórz filename.dat po zakończeniu uruchomienia

i zobaczysz: Tylko w nazwie pliku 1: Nazwa pliku 2 Tylko w: nazwa_katalogu: nazwa_pliku1 Tylko w: nazwa_katalogu: nazwa_pliku2


Dlaczego musisz wyprowadzać dane do pliku .dat?
Wisznu NK,

1

To jest skrypt bash do drukowania poleceń do synchronizacji dwóch katalogów

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|" 

0

GNU grepmoże odwrócić wyszukiwanie z opcją -v. To sprawia, że grepzgłaszanie wierszy, które nie pasują. W ten sposób możesz usunąć pliki dir2z listy plików w dir1.

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

Opcje -F -xpowiedzieć grep, aby przeprowadzić wyszukiwanie tekstu na całej linii.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.