查找一个目录中存在的文件,而不是另一个目录中的文件[关闭]


295

我试图找到一个目录中存在的文件,但不在另一个目录中,我试图使用以下命令:

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在一个单一的命令?

Answers:


390
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 仅打印文件名。


5
我倒是grep对某事像^dir1,以确保我没有得到一个dir1后来出现在该路径。
阿尔夫,

@Alfe可以改进。我$4举个例子。实际上,在我实际的Ubuntu上,diff用意大利语答复。$4可以接受意大利语和英语的回复,但是我不确定其他所有语言都可以使用…
asclepix

139

这应该可以完成以下工作:

diff -rq dir1 dir2

选项说明(通过diff(1)手册页):

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

8
真好!但我认为应该这样扩展:diff -rq dir1 dir2 | grep 'Only in dir1/'
sobi3ch 2015年

2
这是按内容进行的比较,但是在慢速驱动器上可能需要很长时间。
Smeterlink '16

5
请注意以下-q选项:手册页仅说“仅输出文件是否不同”,而不是如何检查它们是否不同。我仔细阅读了源代码,发现它仅检查文件大小来确定差异,而不是实际内容。
ryancdotnet

关于-q选项,我无法重现它仅检查文件大小。使用GNU Diffutils 3.7将两个文件大小相同但内容不同的文件与diff -q file1 file2输出进行比较Files file1 and file2 differ
Stefan Schmidt

50
comm -23 <(ls dir1 |sort) <(ls dir2|sort)

该命令将为您提供位于dir1 而非 dir2中的文件。

关于<( )符号,您可以将其搜索为“流程替换”。


与子目录一起工作也很好,我认为(ls -R dir1|sort)可以解决问题
ulkas 2015年

1
这将适用于OS X恢复模式。
Anthony Vanover

@ulkas,如果使用,输出可能不正确(ls -R dir|sort)
Andriy Makukha

3
vimdiff使用色彩突出显示提供了更好的视觉比较:vimdiff <(ls dir1 |sort) <(ls dir2|sort)
Logan Reed

32

一个好办法做到这一点比较是使用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/

最好的祝福!


1
我没有去过git可以对不在git repo内的任意目录进行比较...太棒了!!! 这个答案对我来说只是解决了一个大问题,谢谢
ViktorNova '19

17

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

合并比较目录


除了meld在行尾方面做得很糟糕...
0xC0000022L

1
行尾从来没有问题。你能详细点吗?
Catalin Hritcu

是的,它并不表示行尾。(反复)导致开发人员使用此工具来提交更改,例如通过将CRLF转换为CRLFLF来“固定”行尾。
0xC0000022L

3
它还坚持读取文件内容,因此对于>> 1GB目录几乎是无用的。
Tomislav Nakic-Alfirevic

13

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

vim -c "DirDiff dir1 dir2"

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


11

我对所有答复都不满意,因为大多数答复工作非常缓慢,并且对于大型目录产生不必要的冗长输出,因此我编写了自己的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

PS:如果需要比较文件大小和文件哈希值以进行潜在更改,我在这里发布了更新的脚本:https : //gist.github.com/amakukha/f489cbde2afd32817f8e866cf4abe779


足够简单的脚本可以完全满足我的要求:验证批量副本:向我+1。(不过需要转换为python2)提示:使用集合可能会使diff部分更简单。
杰森·摩根

6

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

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

由于Erik的post,sed命令删除了第一个目录组件)


1
我相信此方法更简单(仍然使用find注释而不是单独的答案): cd dir2; find . -exec [ -e ../dir1/{} ] \; -o -print 2>/dev/null 这将打印dir2中存在的文件,但dir1中不存在的文件。
亚历山大·阿梅尔金

5

这有点晚了,但可能会对某人有所帮助。不确定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/'

1
请注意,您可以在cd之前find使用sed,而不必使用,例如:comm -23 <(cd dir1 || exit; find -type f | sort) <(cd dir2 || exit; find -type f | sort)。(exit这里的s可以防止find使用当前目录cd失败。)
phk

还要注意,如果存在带有某些特殊字符的文件,则解决方案可能会失败,如果您使用的是comm带有支持的最新版本-z(带有git.savannah.gnu.org/cgit/coreutils.git/commit/…),则可以执行此操作comm -23 -z <(cd dir1 && find -type f -print0 | sort -z) <(cd dir2 && find -type f -print0 | sort -z)。(与此同时,我也发现exits可以替换。)
phk

5

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

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

说明:

  • diff -r dir1 dir2:比较
  • grep“仅在”:获取包含“仅在”的行
  • grep dir1:获取包含dir的行

5

此答案通过添加-D选项来优化@ Adail-Junior的建议之一,这在两个比较目录都不是git存储库的情况下很有用:

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

如果使用-D,就不会看到与的比较/dev/nulltext Binary files a/whatever and /dev/null differ


在比较两个目录时非常有用,您会立即看到文件之间的差异。当然,对于带有文本内容的文件,效果最佳。
Erich Kuester

1

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

diff filename.1 filename.2> filename.dat >>输入

运行完成后打开filename.dat

您将看到:仅在filename.1:filename.2仅在:directory_name:name_of_file1仅在:directory_Name:name_of_file2


为什么必须输出到.dat文件?
Vishnu NK

1

这是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|" 

0

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

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

这些选项-F -x告诉grep您在整行上执行字符串搜索。

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.