同じファイルを含み、同じディレクトリ構造を持つ2つのディレクトリがあります。
これらのディレクトリのいずれかに何かが欠けていると思います。
bashシェルを使用して、ディレクトリを比較し、一方のディレクトリにもう一方のファイルが存在しないかどうかを確認する方法はありますか?
同じファイルを含み、同じディレクトリ構造を持つ2つのディレクトリがあります。
これらのディレクトリのいずれかに何かが欠けていると思います。
bashシェルを使用して、ディレクトリを比較し、一方のディレクトリにもう一方のファイルが存在しないかどうかを確認する方法はありますか?
回答:
この比較を行うための良い方法は、使用することですfind
しmd5sum
、その後、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/
find
ファイルをリストする順序は一般に2つのディレクトリ間で異なるため、これは追加のソート手順なしでは機能しません。
このdiff
コマンドは、ファイルに使用するのと同じように使用できます。
diff <directory1> <directory2>
サブフォルダーと-filesも表示したい場合は、次の-r
オプションを使用できます。
diff -r <directory1> <directory2>
diff
ディレクトリでも機能することを知りませんでした(man diffが確認した)が、これはサブディレクトリ内のサブディレクトリの変更を再帰的にチェックしません。
a/b/c/d/a
、x/b/c/d/b
。何diff a x
が得られるかをご覧ください。
-r
オプションを使用する必要があります。それは(diff -r a x
)を与えてくれます:Only in a/b/c/d: a. only in x/b/c/d: b.
以下は、コンテンツではなくファイル名のみを比較する代替方法です。
diff <(cd folder1 && find . | sort) <(cd folder2 && find . | sort)
これは、欠落しているファイルをリストする簡単な方法ですが、もちろん、同じ名前で内容が異なるファイルは検出しません!
(個人的には自分のdiffdirs
スクリプトを使用していますが、それはより大きなライブラリの一部です。)
diff
はAFAIK がサポートしていないゼロ区切り文字を使用することができます。しかしcomm
、git.savannah.gnu.org / cgit / coreutils.git / commit / ...からそれをサポートしているものがありますので、あなたの近くのcoreutilsに到達したら、あなたはそれを行うことができますcomm -z <(cd folder1 && find -print0 | sort) <(cd folder2 && find -print0 | sort -z)
(その出力をさらにフォーマットに変換する必要があるかもしれません--output-delimiter
パラメータと追加のツールを使用する必要があります)。
たぶん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/
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")
os.listdir
は特定の順序を与えないことに注意してください。そのため、リストの順序が異なる場合があり、比較は失敗します。
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
cmpdirs dir1 dir2 '/\.git/'
bash --version
何ですか?