두 디렉토리의 내용 비교


92

동일한 파일을 포함하고 동일한 디렉토리 구조를 갖는 두 개의 디렉토리가 있습니다.

이 디렉토리 중 하나에 무언가가 빠져 있다고 생각합니다.

bash 쉘을 사용하여 내 디렉토리를 비교하고 그중 하나에 다른 파일이 없는지 확인하는 방법이 있습니까?


1
출력은 bash --version무엇입니까?
jobin February

답변:


63

이 비교를 할 수있는 좋은 방법은 사용하는 것입니다 findmd5sum다음, diff.

find를 사용하여 디렉토리의 모든 파일을 나열한 다음 각 파일의 md5 해시를 계산하고 파일 이름별로 파일을 파이프하여 파일로 파이프하십시오.

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

다른 디렉토리와 동일한 절차를 수행하십시오.

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

그런 다음 결과 두 파일을 다음과 비교하십시오 diff.

diff -u dir1.txt dir2.txt

또는 프로세스 대체를 사용하는 단일 명령으로 :

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

변경 사항 만 보려면 다음을 수행하십시오.

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 명령은 diff와 비교할 해시 (첫 번째 필드) 만 인쇄합니다. 그렇지 않으면 해시가 동일하더라도 디렉토리 경로가 다르기 때문에 diff는 모든 행을 인쇄합니다.

그러나 어떤 파일이 변경되었는지 알 수 없습니다 ...

이를 위해 다음과 같은 것을 시도 할 수 있습니다

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

이 전략은 비교할 두 디렉토리가 동일한 시스템에 있지 않고 두 디렉토리에서 파일이 동일한 지 확인해야 할 때 매우 유용합니다.

작업을 수행하는 또 다른 좋은 방법은 Git의 diff명령을 사용하는 것입니다 (파일에 다른 권한이있는 경우 문제가 발생할 수 있음-> 모든 파일이 출력에 나열 됨).

git diff --no-index dir1/ dir2/

1
find파일을 나열하는 순서 는 두 디렉토리간에 일반적으로 다르기 때문에 추가 정렬 단계 없이는 작동하지 않습니다 .
Faheem Mitha

1
askubuntu.com/a/662383/15729 에 설명 된 방법을 사용 하여 파일을 정렬 할 수 있습니다.
Faheem Mitha

1
``찾기 : md5sum : 해당 파일 또는 디렉토리 없음
Houman

1
@Houman 어떤 Linux Distro를 사용하고 있는지 모르겠지만 md5sum을 제공하는 패키지를 설치해야 할 수도 있습니다. Fedora 26에서는 다음과 같이 설치할 수 있습니다 : #dnf install coreutils
Adail Junior

대신 md5 ()를 사용하십시오
boj

81

diff파일에 사용하는 것처럼 명령을 사용할 수 있습니다 .

diff <directory1> <directory2>

하위 폴더와 -file도 보려면 다음 -r옵션을 사용할 수 있습니다 .

diff -r <directory1> <directory2>

2
diff디렉토리에서도 작동 하지는 않았지만 (man diff는이를 확인했지만) 서브 디렉토리 내부의 서브 디렉토리 변경 사항을 재귀 적으로 확인하지는 않습니다.
jobin February

1
@Jobin 이상합니다 ... 저에게는 효과가 있습니다.
Alex R.

1
나는 이와 같은 것을 가지고있다 : a/b/c/d/a, x/b/c/d/b. 무엇을 diff a x제공 하는지 확인하십시오 .
jobin

2
-r옵션 을 사용해야합니다 . 즉 ( diff -r a x) 나에게 준다 :Only in a/b/c/d: a. only in x/b/c/d: b.
알렉스 R.에게

3
diff는 INTO 파일의 차이점을 보여 주지만 디렉토리에 다른 파일에 포함되지 않은 파일이 있으면 그렇지 않습니다 !!! 파일과의 차이점을 알 필요는 없지만 파일이 디렉토리에 있고 다른 디렉토리에는없는 경우
AndreaNobili

25

bash를 사용하지 않고 --briefand와 diff를 사용하여 수행 할 수 있습니다 --recursive.

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

여기 man diff에는 두 가지 옵션 이 모두 포함됩니다.

-q, --brief
파일이 다를 경우에만 보고서

-r, --recursive
재귀 적으로 발견 된 하위 디렉토리를 비교


13

내용이 아닌 파일 이름 만 비교하는 대안은 다음과 같습니다.

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

이것은 누락 된 파일을 나열하는 쉬운 방법이지만 물론 이름은 같지만 내용이 다른 파일을 감지하지 못합니다 !

(개인적으로 나는 내 자신의 diffdirs스크립트를 사용하지만 더 큰 라이브러리의 일부입니다 .)


3
임시 파일이 아닌 프로세스 대체를 사용하는 것이 좋습니다.
mniip

3
이것은 특정 특수 문자를 가진 파일 이름을 지원하지 않습니다.이 경우 AFAIK diff가 현재 지원하지 않는 0 구분 기호를 사용할 수 있습니다 . 그러나 git.savannah.gnu.org/cgit/coreutils.git/commit/comm 이후로 지원하는 것이 있습니다. 따라서 가까운 코어 유틸리티에 도달 하면 다음을 수행 할 수 있습니다 comm -z <(cd folder1 && find -print0 | sort) <(cd folder2 && find -print0 | sort -z)(이 출력은 형식으로 더 변환해야 할 수도 있습니다) --output-delimiter매개 변수 및 추가 도구 를 사용해야합니다 .
phk

7

아마도 하나의 옵션은 rsync를 두 번 실행하는 것입니다.

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

이전 줄을 사용하면 dir1에 있고 dir2에 다르거 나 누락 된 파일을 얻을 수 있습니다.

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

dir2와 동일

#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

-n옵션을 삭제 하여 변경을 수행 할 수 있습니다 . 파일 목록을 두 번째 폴더로 복사하고 있습니다.

그렇게하는 경우 -u새 파일을 덮어 쓰지 않기 위해을 사용하는 것이 좋습니다 .

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

원 라이너 :

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

3

각 파일을 확장 가능하고 축소 가능하게하려면 출력 diff -r을 Vim으로 파이프 할 수 있습니다 .

먼저 Vim에게 접는 규칙을 지어 보자.

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

이제는

diff -r dir1 dir2 | vim -

당신은 칠 수 zozc개폐 주름 할 수 있습니다. Vim에서 나가려면:q<Enter>


3

파이썬에서 매우 쉬운 작업 :

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

DIR1및의 실제 값을 대체하십시오 DIR2.

다음은 샘플 실행입니다.

$ 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

가독성을 위해 다음은 하나의 라이너 대신 실제 스크립트입니다.

#!/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특정 순서를 제공하지 않습니다. 따라서 목록은 다른 순서로 동일한 것을 가질 수 있으며 비교는 실패합니다.
muru

1
@muru 좋은 지적, 나는 그것에 정렬을 포함합니다
Sergiy Kolodyazhnyy

3

Sergiy의 답변에서 영감을 받아 두 디렉토리를 비교하기 위해 자체 Python 스크립트를 작성했습니다.

다른 많은 솔루션과 달리 파일의 내용을 비교하지 않습니다. 또한 디렉토리 중 하나에서 누락 된 하위 디렉토리에 들어 가지 않습니다. 따라서 출력은 매우 간결하며 스크립트는 큰 디렉토리에서 빠르게 작동합니다.

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

파일을라는 파일에 저장하면 compare_dirs.pyPython3.x로 실행할 수 있습니다.

python3 compare_dirs.py dir1 dir2

샘플 출력 :

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

추신 : 잠재적 변경 사항에 대해 파일 크기와 파일 해시를 비교 해야하는 경우 https://gist.github.com/amakukha/f489cbde2afd32817f8e866cf4abe779에 업데이트 된 스크립트를 게시했습니다.


1
감사합니다. gist.github.com/mscalora/e86e2bbfd3c24a7c1784f3d692b1c684 를 건너 뛰거나 무시 하기 위해 선택적 세 번째 매개 변수 regexp를 추가했습니다 .cmpdirs dir1 dir2 '/\.git/'
Mike


0

방금 발견 한 훌륭한 도구 인 MELD 를 제안하고 싶습니다 .

제대로 작동하고 diffLinux 기반 시스템 에서 명령 으로 수행 할 수있는 모든 것을 멋진 그래픽 인터페이스로 복제 할 수 있습니다! 즐겨

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.