【问题标题】:How to detect only the different files in my bash shell script?如何仅检测我的 bash shell 脚本中的不同文件?
【发布时间】:2019-05-02 12:03:46
【问题描述】:

我正在尝试比较两个存储库中的文件列表,以尝试标记哪些文件已更改。问题是,我的代码说它们都是不同的。但是检查每个哈希摘要表明许多摘要是相同的。

while IFS= read -r filename;
  do
    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
    # inspecting the digest of each file individually         #
    # shows many files are identical and so are the digests   #
    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
    md5 old/$filename; # a456cca87913a4788d980ba4c2f254be
    md5 new/$filename; # a456cca87913a4788d980ba4c2f254be
    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
    # the below conditional is only supposed to echo "differs"    #
    # if the two digests are different                            #
    # but, instead, it echoes "differs" on every file comparison  #
    # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
    [[ $(md5 old/$filename) = $(md5 new/$filename) ]] || echo differs; # differs
  done < files-to-compare.txt

我怎样才能修复这个错误并且只获取不同的文件来报告?

编辑

另外,请注意使用== 而不是=,如

$(md5 old/$filename) == $(md5 new/$filename) ]] || echo differs; 

产生完全相同的错误输出。

编辑2

评论建议使用引号。这也行不通。

"$(md5 old/$filename)" == "$(md5 new/$filename)" ]] || echo differs; 

【问题讨论】:

  • 您只是缺少引号。试试"$(md5 old/$filename)" = ...
  • 程序md5来自哪里?你的意思是md5sum?什么给了你command -V md5
  • @hek2mgl: md5md5sum 的 Mac OSX 版本。
  • 如果md5 打印输入文件名,事实证明,那么这个问题非常愚蠢!我的意思是在这种情况下字符串比较应该如何工作??? (以及您问题中的示例输出如何正确或相关?)

标签: bash shell unix hash md5


【解决方案1】:

要仅查看两个文件的差异,您可以使用 grep,它只会打印不同的行。

grep -v -F -x -f filename1 filename2

也可以使用 comm 来仅打印两个文件之间的差异。

comm -13 &lt;(sort filename1) &lt;(sort filename2)

【讨论】:

  • 你确定diff 完全是为了比较两个文本文件不会更好吗?
  • 你好,作为通讯。 'comm (1) - 逐行比较两个排序的文件'
  • 随便你也可以使用cmp,但对于文本文件,它与diff 相比并没有真正的优势,只会告诉你它们不同以及在哪里。
  • 另外,grep -v -F -x -f filename1 filename2 会告诉你 2 中没有在 1 中的任何行,但是 1 中不在 2 中的行呢?同样,它也没有提到订购。最好使用diff
【解决方案2】:

您可以使用diff 命令比较文件内容,而不是计算 MD5 校验和。它的主要用途是逐行处理文件并比较它们的差异(并生成补丁),但它也可以很容易地用于此目的。如果两个文件之间没有差异,则返回0 的退出,如果有任何差异,则返回1

while IFS= read -r filename;
  do
    if ! diff "old/$filename" "new/$filename" > /dev/null;
    then
      echo "“$filename” differs"
    fi
  done < files-to-compare.txt

如果您使用的是 GNU diff,您可以简单地使用它的 -q, --brief 选项,它只报告文件不同(而不是详细说明它们的不同之处):

while IFS= read -r filename;
  do
    diff -q "old/$filename" "new/$filename"
  done < files-to-compare.txt

【讨论】:

  • diff 有一个 --recursive 选项,它将比较文件的两个子目录,从而使大部分 Bash 功能在此处变得多余。
  • @dawg 我经常使用 -r 选项,但在这种情况下,看起来 OP 已经列出了要在 files-to-compare.txt 中检查的特定文件名(也不清楚它们是使用 GNU 差异)。
  • 递归选项也在 BSD 上。不知道是不是POSIX
  • @dawg 我尽量避免建议选项的长(双破折号多字符)版本——除非我确定用户正在使用 GNU 软件。在这种情况下,建议-r--recursive 更安全。
  • 这是一个很好的解决方案。比较 MD5 校验和是检查两个文件是否不同的一种冗长、缓慢且可能不可靠的方法。见Are there two known strings which have the same MD5 hash value?。但是,cmp -s 是比diff -q 更好的选择。 diff-q 选项不是 POSIX,diff 对二进制文件的处理是“实现定义的”。 cmp -s 通常针对检测文件是否不同进行了优化。见Fastest way to tell if two files are the same in Unix/Linux?
【解决方案3】:

您的脚本已更正:

while IFS= read -r filename;
    do
        # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
        # inspecting the digest of each file individually         #
        # shows many files are identical and so are the digests   #
        # It also prints MD5 (full file path) = md5_signature!    #
        # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
        md5 "old/$filename"              # please use double quotes
        md5 "new/$filename" 
        # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
        # Using -q eliminates all output from md5 except the sig      #
        # Your script now works correctly                             #
        # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

        [[ $(md5 -q "old/$filename") == $(md5 -q "new/$filename") ]] || echo differs; # differs
    done < files.txt

问题:

  1. 您的拼写错误是 new/$fullfile 而不是 new/$filename
  2. 您应该在文件名扩展周围使用"new/$filename"(即使用双引号)
  3. 使用md5 -q 比较md5 在不同文件上的输出。否则md5,默认情况下以MD5 (full_path/base_name) = 2504fcc0c0a57d14aa6b4193b5efaf94 的形式打印输入文件路径。由于保证这些路径在两个不同的目录中是不同的,所以不同的路径名会导致字符串比较失败。

上面的 cmets 假设您在 BSD 或 macOS 上使用 md5

这是一个替代解决方案,它可以在带有md5sum 的 Linux 和带有 md5 的 BSD 上运行。只需将文件的内容提供给任一程序的标准输入,就只打印 md5 签名:

$ md5 <new/file.pdf
2504fcc0c0a57d14aa6b4193b5efaf94

vs 如果使用文件名,则打印路径并打印使用的 MD5 哈希签名:

$ md5 new/file.pdf
MD5 (new/file.pdf) = 2504fcc0c0a57d14aa6b4193b5efaf94

Linux 或 GNU 核心实用程序上的 md5sum 也是如此。

【讨论】:

  • 我认为使用输入重定向只打印 MD5 摘要(不带文件名)是个好主意,但是当您将输入重定向与 md5sum (GNU coreutils) 8.25 一起使用时,它打印摘要,然后打印文件名的-。 :(
  • 因为您将始终有一个空格和一个破折号:' -' 他们将比较相同。所以在这种情况下只有签名是重要的......
【解决方案4】:

在我的 Linux ubuntu 上,有 md5sum 命令:它打印摘要和文件名:

md5sum myFile
215e0f7b4ea9fd9ea5f31106155839fe  myFile

我的意思是你只需要从输出中提取摘要:

md5sum myFile | sed 's/^\([^[:blank:]]*\).*$/\1/g'
215e0f7b4ea9fd9ea5f31106155839fe

然后在测试中使用最后一个命令行:

...
[[ $(md5sum old/"${filename}" | sed 's/^\([^[:blank:]]*\).*$/\1/g') = $(md5sum new/"${filename}" | sed 's/^\([^[:blank:]]*\).*$/\1/g') ]] || echo differs;
...

【讨论】:

  • md5sum 仅适用于 Linux。 BSD 和 MacOS 使用 md5 See this
  • 哈哈,没注意到,谢谢,我正在编辑答案以包含您的信息。
  • @sawg:哎呀,对不起,你已经回答正确了。删除我的编辑,只留下 linux 部分。
  • 您还可以从标准输入 (md5 &lt; old/"$filename") 中读取,在我看到这个之前,我将发布它。 (我不知道-q 选项。)
  • 不清楚 OP 是否因为他们只有md5sum 而不是 md5 而出现错误,或者问题仅仅是文件名包含在md5 的输出。我认为您的(已删除)编辑比当前答案更好。 (md5sum 也可以从标准输入中读取,无需借助 sed 管道。)
猜你喜欢
  • 2011-03-13
  • 1970-01-01
  • 2014-09-27
  • 2012-06-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-16
相关资源
最近更新 更多