问题描述
我有两个目录应该包含相同的文件,并具有相同的目录结构。
我认为其中一个目录中缺少某些东西。
使用bash shell,有没有办法比较我的目录,看看其中一个是否丢失了另一个目录中的文件?
最佳解决办法
进行这种比较的一个好方法是使用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
然后将结果两个文件与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)
当要比较的两个目录不在同一台机器中并且您需要确保两个目录中的文件相同时,此策略非常有用。
另一个好方法是使用Git的diff
命令(当文件具有不同的权限时可能会导致问题 – >然后输出中列出了每个文件):
git diff --no-index dir1/ dir2/
次佳解决办法
您可以像使用文件一样使用diff
命令:
diff <directory1> <directory2>
如果要查看子文件夹和文件,也可以使用-r
选项:
diff -r <directory1> <directory2>
第三种解决办法
通过你不使用bash,你可以使用带有--brief
和--recursive
的diff来实现:
$ diff -rq dir1 dir2
Only in dir2: file2
Only in dir1: file1
man diff
包括两个选项:
-q
,--brief
report only when files differ
-r
,--recursive
recursively compare any subdirectories found
第四种办法
这是另一种方法,只比较文件名,而不是它们的内容:
diff <(cd folder1 && find . | sort) <(cd folder2 && find . | sort)
这是列出丢失文件的简单方法,但当然它不会检测具有相同名称但内容不同的文件!
(我个人使用自己的diffdirs
脚本,但这是larger library的一部分。)
第五种办法
如果要使每个文件都可扩展和可折叠,可以将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>
第六种办法
在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
为了便于阅读,这里是一个实际的脚本,而不是one-liner:
#!/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")
第七种办法
受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.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
附:如果您需要比较文件大小和文件哈希值以进行潜在更改,我在此处发布了更新的脚本:https://gist.github.com/amakukha/f489cbde2afd32817f8e866cf4abe779
第八种办法
也许一个选项是运行rsync两次
rsync -r -n -t -v --progress -c -s /dir1/ /dir2/
使用上一行,您将获得dir1中的文件,并且dir2中的文件不同(或缺失)。还有不同日期的文件夹。
rsync -r -n -t -v --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
您可以删除-n选项以进行更改。那就是将文件列表复制到第二个文件夹。如果你这样做,也许一个很好的选择是使用-u,以避免覆盖较新的文件。
-u, --update skip files that are newer on the receiver