2つのディレクトリの内容を比較する


92

同じファイルを含み、同じディレクトリ構造を持つ2つのディレクトリがあります。

これらのディレクトリのいずれかに何かが欠けていると思います。

bashシェルを使用して、ディレクトリを比較し、一方のディレクトリにもう一方のファイルが存在しないかどうかを確認する方法はありますか?


1
の出力はbash --version何ですか?
jobin 14

回答:


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

次に、結果を2つのファイルと比較します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/ .*\// /')

この戦略は、比較する2つのディレクトリが同じマシンになく、両方のディレクトリでファイルが等しいことを確認する必要がある場合に非常に役立ちます。

ジョブを実行する別の良い方法は、Gitのdiffコマンドを使用することです(ファイルのアクセス許可が異なる場合に問題が発生する可能性があります->その後、すべてのファイルが出力にリストされます)。

git diff --no-index dir1/ dir2/

1
findファイルをリストする順序は一般に2つのディレクトリ間で異なるため、これは追加のソート手順なしでは機能しません。
ファヒムミサ

1
askubuntu.com/a/662383/15729で説明されている方法を使用して、ファイルをソートできます。
ファヒムミサ

1
「find:md5sum:No such file or directory
Houman

1
@HoumanどんなLinux Distroを使用しているかわかりませんが、de md5sumを提供するパッケージをインストールする必要があるかもしれません。Fedora 26では、次を使用してインストールできます。#dnf install coreutils
Adail Junior

代わりにMD5()を使用してください
日銀

81

このdiffコマンドは、ファイルに使用するのと同じように使用できます。

diff <directory1> <directory2>

サブフォルダーと-filesも表示したい場合は、次の-rオプションを使用できます。

diff -r <directory1> <directory2>

2
diffディレクトリでも機能することを知りませんでした(man diffが確認した)が、これはサブディレクトリ内のサブディレクトリの変更を再帰的にチェックしません。
jobin 14

1
@ジョビンそれは奇妙です...私にとっては、それは動作します。
アレックスR. 14

1
次のようなものがあります:a/b/c/d/ax/b/c/d/b。何diff a xが得られるかをご覧ください。
ジョバン

2
-rオプションを使用する必要があります。それは(diff -r a x)を与えてくれます:Only in a/b/c/d: a. only in x/b/c/d: b.
アレックスR. 14

3
diffは、INTOファイルの違いを表示しますが、ディレクトリに含まれるファイルが他のディレクトリに含まれていない場合は表示されません!!! 私は違いのファイルにもファイルがディレクトリではなく、他のいずれかに存在するかどうかを知る必要はありません
AndreaNobili

25

bashを使用していないため、diffを使用して--briefand を使用できます--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
これは特定の特殊文字を含むファイル名をサポートしていないことに注意してください。その場合、現時点でdiffはAFAIK がサポートしていないゼロ区切り文字を使用することができます。しかしcommgit.savannah.gnu.org / cgit / coreutils.git / commit / ...からそれをサポートしているものがありますので、あなたの近くのcoreutilsに到達したら、あなたはそれを行うことができますcomm -z <(cd folder1 && find -print0 | sort) <(cd folder2 && find -print0 | sort -z)(その出力をさらにフォーマットに変換する必要があるかもしれません--output-delimiterパラメータと追加のツールを使用する必要があります)。
-phk

7

たぶん1つのオプションは、rsyncを2回実行することです。

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オプションを削除して、変更を加えることができます。これは、ファイルのリストを2番目のフォルダーにコピーしています。

その場合-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 -

ひだを開いたり閉じzoたりzcすることができます。Vimから抜け出すには、:q<Enter>


3

pythonで達成するかなり簡単なタスク:

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

読みやすくするために、1行ではなく実際のスクリプトを次に示します。

#!/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は特定の順序を与えないことに注意してください。そのため、リストの順序が異なる場合があり、比較は失敗します。
ムル

1
@muruの良い点、私はそれへのソートを含めます
Sergiy Kolodyazhnyy

3

Sergiyの回答に触発されて、2つのディレクトリを比較するための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.pyすると、Python3.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

PS潜在的な変更のためにファイルサイズとファイルハッシュを比較する必要がある場合、更新されたスクリプトをここに公開しました:https : //gist.github.com/amakukha/f489cbde2afd32817f8e866cf4abe779


1
おかげで、私はスキップ/無視するには、オプションの第三のparam正規表現を追加 gist.github.com/mscalora/e86e2bbfd3c24a7c1784f3d692b1c684:私は次のように必要なものだけにするcmpdirs dir1 dir2 '/\.git/'
マイク・


0

発見したばかりのすばらしいツール、MELDを提案したいと思います。

それは適切に動作し、diffLinuxベースのシステムでコマンドを使ってできることはすべて、素敵なグラフィックインターフェイスで複製できます!楽しい

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.