比较两个目录的内容


92

我有两个目录,应包含相同的文件,并具有相同的目录结构。

我认为这些目录之一中缺少某些内容。

使用bash shell,有没有办法比较我的目录,看看其中一个是否丢失了另一个目录中存在的文件?


1
输出是bash --version什么?
Jobin 2014年

Answers:


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,但是也许您需要安装一个提供de md5sum的软件包。在Fedora 26中,您可以使用以下命令进行安装:#dnf install coreutils
Adail Junior

使用md5()代替
boj

81

您可以diff像使用文件一样使用该命令:

diff <directory1> <directory2>

如果还想查看子文件夹和-files,则可以使用以下-r选项:

diff -r <directory1> <directory2>

2
也不知道diff目录的工作原理(man diff确认了这一点),但这不会递归检查子目录中子目录的更改。
jobin 2014年

1
@Jobin真奇怪……对我来说,它确实有效。
亚历克斯R.14年

1
我有这样的事情:a/b/c/d/ax/b/c/d/b。看看有什么diff a x给你。
jobin 2014年

2
您必须使用该-r选项。(diff -r a x)给了我:Only in a/b/c/d: a. only in x/b/c/d: b.
Alex R.

3
diff告诉我INTO文件的区别,但是如果目录包含另一个文件不包含的文件,则不会!我不需要知道文件中的差异,但是也不需要知道文件是否存在于目录中,而不是存在于另一个目录中
AndreaNobili 2014年

24

通过不使用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
请注意,这不支持带有某些特殊字符的文件名,在这种情况下,您可能希望使用AFAIK diff目前不支持的零分隔符。但是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

也许一种选择是运行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中实现的任务相当简单:

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不会给出任何特定的顺序。因此,列表可能具有相同的事物,但顺序不同,因此比较将失败。
大师

1
@muru好点,我将包括
对它的

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.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
谢谢,我添加了一个可选的第三个参数正则表达式,以跳过/忽略 gist.github.com/mscalora/e86e2bbfd3c24a7c1784f3d692b1c684使我需要的内容是这样的:cmpdirs dir1 dir2 '/\.git/'
Mike

0

我将在一段时间前添加的NodeJs替代品添加到此列表中。

目录比较

npm install dir-compare -g
dircompare dir1 dir2

0

我想建议一个我刚刚发现的好工具:MELD

它可以正常工作,并且您可以diff在基于Linux的系统上使用该命令执行的所有操作,都可以在其中复制带有漂亮的图形界面!请享用

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.