【问题标题】:Find the files existing in one directory but not in the other [closed]查找一个目录中存在但另一个目录中不存在的文件[关闭]
【发布时间】:2013-05-23 04:19:39
【问题描述】:

我正在尝试查找一个目录中存在但另一个目录中不存在的文件,我尝试使用此命令:

diff -q dir1 dir2

上述命令的问题是它找到了dir1中的文件但没有找到dir2中的文件以及dir2中的文件但没有找到dir1中的文件,

我正在尝试在 dir1 中查找文件,但不是仅在 dir2 中。

这是我的数据的一个小样本

dir1    dir2    dir3
1.txt   1.txt   1.txt
2.txt   3.txt   3.txt
5.txt   4.txt   5.txt
6.txt   7.txt   8.txt

我想到的另一个问题是如何在单个命令中找到 dir1 而不是 dir2dir3 中的文件?

【问题讨论】:

    标签: linux bash diff


    【解决方案1】:

    此答案通过添加 -D 选项优化了来自 @Adail-Junior 的建议之一,这在所比较的目录都不是 git 存储库时很有帮助:

    git diff -D --no-index dir1/ dir2/
    

    如果您使用-D,那么您将看不到与/dev/null 的比较: text Binary files a/whatever and /dev/null differ

    【讨论】:

    • 在比较两个目录时非常有用,您可以立即看到文件之间的差异。当然,在带有文本内容的文件上效果最好。
    【解决方案2】:

    这是用于打印同步两个目录的命令的 bash 脚本

    dir1=/tmp/path_to_dir1
    dir2=/tmp/path_to_dir2
    diff -rq $dir1 $dir2 | sed -e "s|Only in $dir2\(.*\): \(.*\)|cp -r $dir2\1/\2 $dir1\1|" |  sed -e "s|Only in $dir1\(.*\): \(.*\)|cp -r $dir1\1/\2 $dir2\1|" 
    

    【讨论】:

      【解决方案3】:

      GNU grep 可以使用选项-v 进行反向搜索。这使得grep 报告不匹配的行。这样,您可以从dir1 的文件列表中删除dir2 中的文件。

      grep -v -F -x -f <(find dir2 -type f -printf '%P\n') <(find dir1 -type f -printf '%P\n')
      

      -F -x 选项告诉grep 对整行执行字符串搜索。

      【讨论】:

        【解决方案4】:

        对所有回复都不满意,因为它们中的大多数都运行得很慢并且为大型目录产生不必要的长输出,所以我编写了自己的 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])
        

        示例用法:

        user@laptop:~$ python3 compare_dirs.py dir1/ dir2/
        DIR  dir1/out/flavor-domino removed
        DIR  dir2/out/flavor-maxim2 added
        DIR  dir1/target/vendor/flavor-domino removed
        DIR  dir2/target/vendor/flavor-maxim2 added
        FILE dir1/tmp/.kconfig-flavor_domino removed
        FILE dir2/tmp/.kconfig-flavor_maxim2 added
        DIR  dir2/tools/tools/LiveSuit_For_Linux64 added
        

        或者,如果您只想查看第一个目录中的文件:

        user@laptop:~$ python3 compare_dirs.py dir2/ dir1/ | grep dir1
        DIR  dir1/out/flavor-domino added
        DIR  dir1/target/vendor/flavor-domino added
        FILE dir1/tmp/.kconfig-flavor_domino added
        

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

        【讨论】:

        • 足够简单的脚本,完全符合我的要求:验证批量复制:+1 来自我。 (不过需要转换为 python2)提示:使用集合可能会使差异部分更简单。
        【解决方案5】:

        这应该可以完成工作:

        diff -rq dir1 dir2
        

        选项解释(通过 diff(1) man page):

        • -r - 递归比较找到的所有子目录。
        • -q - 仅输出文件是否不同。

        【讨论】:

        • 不错!但我认为应该这样扩展:diff -rq dir1 dir2 | grep 'Only in dir1/'
        • 这是按内容比较,但在慢速驱动器上可能需要很长时间。
        • 只是关于-q 选项的注释:手册页只说“仅输出文件是否不同”,而不是如何检查它们是否不同。我仔细阅读了源代码,发现它只检查文件大小以确定差异,而不是实际内容。
        • 关于-q 选项我无法重现它只检查文件大小。使用 GNU Diffutils 3.7 比较两个文件大小相同但内容不同的文件,diff -q file1 file2 输出 Files file1 and file2 differ
        【解决方案6】:

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

        例子:

        使用find 列出目录中的所有文件,然后计算每个文件的 md5 哈希并将其通过管道传输到文件:

        find /dir1/ -type f -exec md5sum {} \; > dir1.txt
        

        对另一个目录执行相同的过程:

        find /dir2/ -type f -exec md5sum {} \; > dir2.txt
        

        然后用“diff”比较两个文件的结果:

        diff dir1.txt dir2.txt
        

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

        完成这项工作的另一个好方法是使用 git

        git diff --no-index dir1/ dir2/
        

        最好的问候!

        【讨论】:

        • 我没有去 git 可以对不在 git repo 内的任意目录进行差异化......太棒了!这个答案刚刚为我解决了一个大问题,谢谢
        【解决方案7】:

        vim 的DirDiff 插件是另一个非常有用的比较目录的工具。

        vim -c "DirDiff dir1 dir2"
        

        它不仅列出了目录之间不同的文件,还允许您使用 vimdiff 检查/修改不同的文件。

        【讨论】:

          【解决方案8】:

          接受的答案还将列出两个目录中存在但内容不同的文件。要仅列出 dir1 中存在的文件,您可以使用:

          diff -r dir1 dir2 | grep 'Only in' | grep dir1 | awk '{print $4}' > difference1.txt
          

          解释:

          • diff -r dir1 dir2:比较
          • grep 'Only in':获取包含'Only in'的行
          • grep dir1 : 获取包含 dir 的行

          【讨论】:

            【解决方案9】:

            Meld (http://meldmerge.org/) 在比较目录和其中的文件方面做得很好。

            【讨论】:

            • 除了 meld 在行尾方面做得很糟糕......
            • 从来没有遇到过行尾问题。能详细点吗?
            • 是的,它不表示行尾。这(反复)导致使用此工具的开发人员提交更改,例如通过将 CRLF 变为 CRLFLF 来“修复”行尾。
            • 它还坚持读取文件内容,因此对于>>1GB目录几乎无用。
            【解决方案10】:

            使用 DIFF 命令比较 2 个目录的简化方法

            diff 文件名.1 文件名.2 > 文件名.dat >>Enter

            运行完成后打开filename.dat

            你会看到: 仅在 filename.1 中:filename.2 仅在:directory_name:name_of_file1 仅在:directory_Name:name_of_file2

            【讨论】:

            • 为什么一定要输出到.dat文件?
            【解决方案11】:

            另一种(对于大型目录可能更快)方法:

            $ find dir1 | sed 's,^[^/]*/,,' | sort > dir1.txt && find dir2 | sed 's,^[^/]*/,,' | sort > dir2.txt
            $ diff dir1.txt dir2.txt
            

            sed 命令删除第一个目录组件thanks to Erik`s post)

            【讨论】:

            • 我相信这种方法更简单(仍然使用find,因此是注释而不是单独的答案):cd dir2; find . -exec [ -e ../dir1/{} ] \; -o -print 2&gt;/dev/null 这将打印 dir2 中存在但 dir1 中不存在的文件。
            【解决方案12】:
            diff -r dir1 dir2 | grep dir1 | awk '{print $4}' > difference1.txt
            

            说明:

            • diff -r dir1 dir2 显示哪些文件仅在 dir1 中,哪些文件仅在 dir2 中,以及两个目录中存在的文件的更改(如果有)。

            • diff -r dir1 dir2 | grep dir1 显示哪些文件仅在 dir1 中

            • awk 仅打印文件名。

            【讨论】:

            • 我会用grep 代替^dir1 以确保不会在路径后面出现dir1
            • @Alfe 可以改进。我以$4 为例。事实上,在我实际的 Ubuntu 上,diff 用意大利语回复。 $4 适用于意大利语和英语回复,但我不确定是否适用于所有其他语言...
            【解决方案13】:

            这有点晚了,但可能会对某人有所帮助。不确定 diff 或 rsync 是否仅以像这样的裸格式吐出文件名。感谢 plhn 提供了我在下面扩展的很好的解决方案。

            如果您只需要文件名以便以干净的格式复制所需的文件,您可以使用 find 命令。

            comm -23 <(find dir1 | sed 's/dir1/\//'| sort) <(find dir2 | sed 's/dir2/\//'| sort) | sed 's/^\//dir1/'
            

            这假设 dir1 和 dir2 都在同一个父文件夹中。 sed 只是删除父文件夹,因此您可以将苹果与苹果进行比较。最后一个 sed 只是把 dir1 的名字放回去。

            如果你只想要文件:

            comm -23 <(find dir1 -type f | sed 's/dir1/\//'| sort) <(find dir2 -type f | sed 's/dir2/\//'| sort) | sed 's/^\//dir1/'
            

            目录也一样:

            comm -23 <(find dir1 -type d | sed 's/dir1/\//'| sort) <(find dir2 -type d | sed 's/dir2/\//'| sort) | sed 's/^\//dir1/'
            

            【讨论】:

            • 请注意,您可以在find 之前使用cd,而不必使用sed,例如:comm -23 &lt;(cd dir1 || exit; find -type f | sort) &lt;(cd dir2 || exit; find -type f | sort)。 (exits 用于防止 findcd 失败时使用当前目录。)
            • 另外请注意,如果您有一个最新版本的comm 支持-z(附带git.savannah.gnu.org/cgit/coreutils.git/commit/…),您的解决方案可能会在存在具有某些特殊字符的文件时失败。 comm -23 -z &lt;(cd dir1 &amp;&amp; find -type f -print0 | sort -z) &lt;(cd dir2 &amp;&amp; find -type f -print0 | sort -z)。 (同时我也发现exits 可以被替换。)
            【解决方案14】:
            comm -23 <(ls dir1 |sort) <(ls dir2|sort)
            

            此命令将为您提供那些在 dir1 和 不在 dir2 中的文件。

            关于&lt;( ) 的符号,你可以用谷歌搜索它作为“进程替换”。

            【讨论】:

            • 也可以使用子目录,我认为(ls -R dir1|sort) 可以解决问题
            • 这适用于 OS X 恢复模式。
            • @ulkas,如果你使用(ls -R dir|sort),输出可能不正确。
            • vimdiff 通过颜色突出显示提供了更好的视觉比较:vimdiff &lt;(ls dir1 |sort) &lt;(ls dir2|sort)
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2020-02-20
            • 2015-12-06
            • 2021-06-25
            • 2012-07-11
            • 2017-04-01
            • 2015-05-18
            • 1970-01-01
            相关资源
            最近更新 更多