İki dizinin içeriğinin karşılaştırılması


92

Aynı dosyaları içermesi ve aynı dizin yapısına sahip olması gereken iki dizinim var.

Bu dizinlerden birinde bir şeyin eksik olduğunu düşünüyorum.

Bash kabuğunu kullanarak, dizinlerimi karşılaştırmanın ve bunlardan birinin diğerinde eksik dosyaları olup olmadığını görmenin bir yolu var mı?


1
Çıktı nedir bash --version?
Jobin

Yanıtlar:


63

Bu karşılaştırmayı yapmak için iyi bir yol kullanmaktır findile md5sumdaha sonra, diff.

Örnek

Dizindeki tüm dosyaları listelemek için find komutunu kullanın, ardından her dosya için md5 karmasını hesaplayın ve dosya adına göre sıralayarak bir dosyaya aktarın:

find /dir1/ -type f -exec md5sum {} + | sort -k 2 > dir1.txt

Aynı işlemi başka bir dizine de yapın:

find /dir2/ -type f -exec md5sum {} + | sort -k 2 > dir2.txt

Sonra sonuç iki dosya ile karşılaştırın diff:

diff -u dir1.txt dir2.txt

Veya işlem değişikliğini kullanarak tek bir komut olarak:

diff <(find /dir1/ -type f -exec md5sum {} + | sort -k 2) <(find /dir2/ -type f -exec md5sum {} + | sort -k 2)

Yalnızca değişiklikleri görmek istiyorsanız:

diff <(find /dir1/ -type f -exec md5sum {} + | sort -k 2 | cut -f1 -d" ") <(find /dir2/ -type f -exec md5sum {} + | sort -k 2 | cut -f1 -d" ")

Cut komutu, diff ile karşılaştırılacak yalnızca karma (ilk alan) yazdırır. Aksi taktirde diff, her satır, karma aynı olsa bile dizin yolları farklı olduğundan yazdırır.

Ama hangi dosyanın değiştiğini bilmiyorsun.

Bunun için, böyle bir şey deneyebilirsiniz

diff <(find /dir1/ -type f -exec md5sum {} + | sort -k 2 | sed 's/ .*\// /') <(find /dir2/ -type f -exec md5sum {} + | sort -k 2 | sed 's/ .*\// /')

Bu strateji, karşılaştırılacak iki dizin aynı makinede olmadığında ve dosyaların her iki dizinde de eşit olduğundan emin olmanız gerektiğinde çok faydalıdır.

İşi yapmanın bir başka iyi yolu Git'in diffkomutunu kullanmak (dosyalar farklı izinlere sahip olduğunda sorunlara neden olabilir -> her dosya çıkışta listeleniyorsa):

git diff --no-index dir1/ dir2/

1
Bu, fazladan bir sıralama adımı olmadan işe yaramaz, çünkü finddosyaları listeleme sırası genel olarak iki dizin arasında farklı olacaktır.
Faheem Mitha

1
Biri dosyaları sıralamak için askubuntu.com/a/662383/15729 adresinde açıklanan yöntemi kullanabilir .
Faheem Mitha

1
Hata alıyorum `` bul: md5sum: Böyle bir dosya veya dizin yok
Houman

1
@Houman Hangi Linux Distro'yu kullandığınızı bilmiyorum, ama belki de md5sum sağlayacak bir paket kurmanız gerekir. Fedora 26'da bunu kurabilirsiniz: #dnf install coreutils
Adail Junior

Yerine md5 () kullanın
boj

81

Sen kullanabilirsiniz diffdosyaların için kullanmak istiyorsunuz gibi komutu:

diff <directory1> <directory2>

Siz de alt klasörleri ve -files'i görmek istiyorsanız, bu -rseçeneği kullanabilirsiniz :

diff -r <directory1> <directory2>

2
diffDizinler için de çalışmadığını bilmiyordum (man diff bunu onayladı), ancak bu alt dizinlerdeki alt dizinlerdeki değişiklikleri tekrar tekrar kontrol etmiyor.
Jobin

1
@Jobin Bu garip ... Benim için işe yarıyor.
Alex R.,

1
Böyle bir şey var: a/b/c/d/a, x/b/c/d/b. Bak sana ne diff a xveriyor?
Jobin

2
-rSeçeneği kullanmanız gerekir . That ( diff -r a x) bana verir:Only in a/b/c/d: a. only in x/b/c/d: b.
Alex R.

3
diff bana INTO dosyalarındaki farkı gösterir ancak bir dizin diğerinin içermediği bir dosyayı içeriyorsa !!! Dosyadaki farklılıkları bilmeme gerek yok ama aynı zamanda bir dosya diğerinde değil de bir dizinde bulunuyorsa
AndreaNobili

25

Bash kullanmıyorsanız, diff ile --briefve ile kullanarak yapabilirsiniz --recursive:

$ diff -rq dir1 dir2 
Only in dir2: file2
Only in dir1: file1

Her man diffiki seçenek de içerir:

-q, --brief
yalnızca dosyalar farklı olduğunda rapor verin

-r, --recursive
bulunan alt dizinleri yinelemeli olarak karşılaştırın


13

İşte sadece dosya isimlerini karşılaştırmak için bir alternatif, içerikleri değil:

diff <(cd folder1 && find . | sort) <(cd folder2 && find . | sort)

Bu, eksik dosyaları listelemenin kolay bir yoludur, ancak elbette aynı ada sahip fakat farklı içeriklere sahip dosyaları algılamayacak !

(Şahsen kendi diffdirssenaryomu kullanıyorum, ancak bu daha büyük bir kütüphanenin parçası .)


3
İşlem değiştirme yerine geçici dosyaları
kullansanız iyi olur

3
Bunun, belirli özel karakterlere sahip dosya adlarını desteklemediğini unutmayın; bu durumda, AFAIK'in diffşu an desteklemediği sıfır sınırlayıcıları kullanmak isteyebilirsiniz . Ama git.savannah.gnu.org/cgit/coreutils.git/commit/… 'den bu commyana onu destekleyenler var, bu yüzden yakınınızdaki bir coreutils'e gelince, yapabilecekleriniz comm -z <(cd folder1 && find -print0 | sort) <(cd folder2 && find -print0 | sort -z)(çıktılarını daha fazla dönüştürmek zorunda kalabilirsiniz) --output-delimiterparametre ve ek araçlar kullanmanız gerekir ).
phk

7

Belki bir seçenek rsync'i iki kez çalıştırmaktır:

rsync -r -n -t -v -O --progress -c -s /dir1/ /dir2/

Bir önceki satırda, dir1 ve dir2'de farklı (veya eksik) olan dosyaları alacaksınız.

rsync -r -n -t -v -O --progress -c -s /dir2/ /dir1/

Dir2 için aynı

#from the rsync --help :
-r, --recursive             recurse into directories
-n, --dry-run               perform a trial run with no changes made
-t, --times                 preserve modification times
-v, --verbose               increase verbosity
    --progress              show progress during transfer
-c, --checksum              skip based on checksum, not mod-time & size
-s, --protect-args          no space-splitting; only wildcard special-chars
-O, --omit-dir-times        omit directories from --times

-nDeğişikliklerden geçmek için seçeneği silebilirsiniz . Bu, dosyaların listesini ikinci klasöre kopyalamaktır.

Bunu yapmanız durumunda -u, yeni dosyaların üzerine yazmamak için belki iyi bir seçenek kullanmaktır .

-u, --update                skip files that are newer on the receiver

Bir astar:

rsync -rtvcsOu -n --progress /dir1/ /dir2/ && rsync -rtvcsOu -n --progress /dir2/ /dir1/

3

Her dosyayı genişletilebilir ve daraltılabilir yapmak istiyorsanız, çıktısını diff -rVim'e aktarabilirsiniz.

İlk önce Vim'e katlama kuralı verelim:

mkdir -p ~/.vim/ftplugin
echo "set foldexpr=getline(v:lnum)=~'^diff.*'?'>1':1 foldmethod=expr fdc=2" >> ~/.vim/ftplugin/diff.vim

Şimdi sadece:

diff -r dir1 dir2 | vim -

Sen vurabilir zove zcaçıp kapatmak kıvrımlar için. Vim'den çıkmak için, vur:q<Enter>


3

Python'da elde etmek için oldukça kolay bir görev:

python -c 'import os,sys;d1=os.listdir(sys.argv[1]);d2=os.listdir(sys.argv[2]);d1.sort();d2.sort();x="SAME" if d1 == d2 else "DIFF";print x' DIR1 DIR2

İçin Yedek fiili değerler DIR1ve DIR2.

İşte örnek çalışma:

$ python -c 'import os,sys;d1=os.listdir(sys.argv[1]);d2=os.listdir(sys.argv[2]);d1.sort();d2.sort();x="SAME" if d1 == d2 else "DIFF";print x' Desktop/ Desktop
SAME
$ python -c 'import os,sys;d1=os.listdir(sys.argv[1]);d2=os.listdir(sys.argv[2]);d1.sort();d2.sort();x="SAME" if d1 == d2 else "DIFF";print x' Desktop/ Pictures/
DIFF

Okunabilmesi için işte bir liner yerine gerçek bir script:

#!/usr/bin/env python
import os, sys

d1 = os.listdir(sys.argv[1])
d2 = os.listdir(sys.argv[2])
d1.sort()
d2.sort()

if d1 == d2:
    print("SAME")
else:
    print("DIFF")

2
os.listdirÖzel bir sipariş vermediğine dikkat edin . Bu yüzden listeler aynı sırayla aynı şeylere sahip olabilir ve karşılaştırma başarısız olur.
muru

1
@muru iyi bir noktaya, bu işe sıralama ekleriz
Sergiy Kolodyazhnyy

3

Sergiy'in cevabından ilham alan iki dizini karşılaştırmak için kendi Python senaryomu yazdım.

Diğer birçok çözümden farklı olarak dosyaların içeriğini karşılaştırmaz. Ayrıca dizinlerden birinde eksik olan alt dizinlerin içine girmez. Bu yüzden çıktı oldukça özlüdür ve script hızlıca büyük dizinlerle çalışır.

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

Dosyayı adlı bir dosyaya kaydederseniz compare_dirs.py, Python3.x ile çalıştırabilirsiniz:

python3 compare_dirs.py dir1 dir2

Örnek çıktı:

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

PS Potansiyel değişiklikler için dosya boyutlarını ve dosya özeliklerini karşılaştırmanız gerekirse, burada güncellenmiş bir komut dosyasını yayımladım: https://gist.github.com/amakukha/f489cbde2afd32817f8e866cf4abe779


1
Teşekkürler, ben görmezden / atlamak için isteğe bağlı üçüncü param Regexp'i eklendi gist.github.com/mscalora/e86e2bbfd3c24a7c1784f3d692b1c684 ben sadece ne gerekli yapmak gibi:cmpdirs dir1 dir2 '/\.git/'
Mike


0

MELD : Yeni keşfettiğim harika bir araç önermek istiyorum .

Düzgün çalışır ve diffLinux tabanlı sistem komutuyla yapabileceğiniz her şey , güzel bir Grafik Arayüz ile orada çoğaltılabilir! Keyfini çıkarın

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.