比较两个目录的内容

And*_*ili 182 command-line

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

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

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

Ale*_* R. 154

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

diff <directory1> <directory2>
Run Code Online (Sandbox Code Playgroud)

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

diff -r <directory1> <directory2>
Run Code Online (Sandbox Code Playgroud)

  • diff 向我展示 INTO 文件的差异,但如果一个目录包含另一个不包含的文件,则不会显示差异!!!我不需要知道文件的差异,也不需要知道一个文件是否存在于一个目录中而不是另一个目录中 (6认同)
  • 不知道 `diff` 也适用于目录(man diff 证实了这一点),但这不会递归检查子目录内的子目录中的更改。 (3认同)
  • 你必须使用`-r` 选项。那(`diff -rax`)给了我:`仅在a/b/c/d中:a。仅在 x/b/c/d: b.` (3认同)
  • @AndreaNobili,GNU diff 仅针对其中一个文件夹中的文件显示“仅在目录1/路径中”。 (3认同)
  • @Jobin 这很奇怪......对我来说,它确实有效。 (2认同)

小智 133

进行这种比较的一个好方法是使用findwith md5sum,然后使用a diff

例子

使用 find 列出目录中的所有文件,然后计算每个文件的 md5 哈希并将其按文件名排序到文件中:

find /dir1/ -type f -exec md5sum {} + | sort -k 2 > dir1.txt
Run Code Online (Sandbox Code Playgroud)

对另一个目录执行相同的步骤:

find /dir2/ -type f -exec md5sum {} + | sort -k 2 > dir2.txt
Run Code Online (Sandbox Code Playgroud)

然后将两个文件的结果与diff

diff -u dir1.txt dir2.txt
Run Code Online (Sandbox Code Playgroud)

或者作为使用进程替换的单个命令:

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

如果您只想查看更改:

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" ")
Run Code Online (Sandbox Code Playgroud)

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/ .*\// /')
Run Code Online (Sandbox Code Playgroud)

当要比较的两个目录不在同一台机器上并且您需要确保两个目录中的文件相同时,此策略非常有用。

完成这项工作的另一个好方法是使用 Git 的diff命令(当文件具有不同的权限时可能会导致问题 -> 然后在输出中列出每个文件):

git diff --no-index dir1/ dir2/
Run Code Online (Sandbox Code Playgroud)

  • 为什么不直接 diff -r 呢? (2认同)

Bra*_*iam 56

通过您不使用 bash,您可以使用 diff--brief--recursive

$ diff -rq dir1 dir2 
Only in dir2: file2
Only in dir1: file1
Run Code Online (Sandbox Code Playgroud)

man diff包括两个选项:

-q,--brief
仅在文件不同时报告

-r,--recursive
递归比较找到的任何子目录


Fer*_*oao 21

也许一种选择是运行 rsync 两次:

rsync -rtOvcs --progress -n /dir1/ /dir2/
Run Code Online (Sandbox Code Playgroud)

使用前一行,您将获得 dir1 中的文件,而 dir2 中的文件不同(或丢失)。

rsync -rtOvcs --progress -n /dir2/ /dir1/
Run Code Online (Sandbox Code Playgroud)

dir2 也一样

#from the rsync --help :
-n, --dry-run               perform a trial run with no changes made

-r, --recursive             recurse into directories
-t, --times                 preserve modification times
-O, --omit-dir-times        omit directories from --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
Run Code Online (Sandbox Code Playgroud)

您可以删除-n选项以进行更改。那就是将文件列表复制到第二个文件夹。

如果您这样做,也许一个不错的选择是使用-u, 以避免覆盖较新的文件。

-u, --update                skip files that are newer on the receiver
Run Code Online (Sandbox Code Playgroud)

单线:

rsync -rtOvcsu --progress -n  /dir1/ /dir2/ && rsync -rtOvcsu --progress -n /dir2/ /dir1/
Run Code Online (Sandbox Code Playgroud)


joe*_*dle 15

这是一种替代方法,只比较文件名,而不是它们的内容:

diff <(cd folder1 && find . | sort) <(cd folder2 && find . | sort)
Run Code Online (Sandbox Code Playgroud)

这是一种列出丢失文件的简单方法,但当然它不会检测到名称相同但内容不同的文件!

(我个人使用自己的diffdirs脚本,但那是更大库的一部分。)

  • 你最好使用进程替换,而不是临时文件...... (3认同)
  • 请注意,这不支持具有某些特殊字符的文件名,在这种情况下,您可能希望使用 AFAIK `diff` 目前不支持的零分隔符。但是有 `comm` 支持它,因为 http://git.savannah.gnu.org/cgit/coreutils.git/commit/?id=f3b4def577c4eee22f83b72d1310aa1d9155908d 所以一旦涉及到你附近的 coreutils,你可以做 `comm -z &lt;(cd folder1 &amp;&amp; find -print0 | sort) &lt;(cd folder2 &amp;&amp; find -print0 | sort -z)`(您可能需要使用 `--output-delimiter` 将其输出进一步转换为您需要的格式参数和附加工具)。 (3认同)

Leo*_*313 7

我想推荐一个我刚刚发现的好工具:MELD

它工作正常,您可以diff在基于 Linux 的系统上使用该命令执行的所有操作都可以通过漂亮的图形界面进行复制!

例如,目录比较很简单:

目录比较

并且文件比较也变得更容易:

文件比较

与一些控制版本(例如 Git)有很好的集成,可以用作合并工具。请参阅其网站上的完整文档。


joe*_*dle 5

如果你想让每个文件都可展开和折叠,你可以将 的输出通过管道传输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
Run Code Online (Sandbox Code Playgroud)

现在只需:

diff -r dir1 dir2 | vim - -R
Run Code Online (Sandbox Code Playgroud)

您可以点击zozc打开和关闭折叠。要退出 Vim,请点击:q<Enter>

-R是可选的,但我发现它很有用-,因为它可以防止 Vim 在退出时打扰您保存缓冲区。


And*_*kha 5

受 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])
Run Code Online (Sandbox Code Playgroud)

如果将其保存到名为 的文件中compare_dirs.py,则可以使用 Python3.x 运行它:

python3 compare_dirs.py dir1 dir2
Run Code Online (Sandbox Code Playgroud)

示例输出:

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
Run Code Online (Sandbox Code Playgroud)

PS如果您需要比较文件大小和文件哈希以了解潜在的变化,我在这里发布了一个更新的脚本:https : //gist.github.com/amakukha/f489cbde2afd32817f8e866cf4abe779