【问题标题】:Fastest way to tell if two files have the same contents in Unix/Linux?在 Unix/Linux 中判断两个文件是否具有相同内容的最快方法?
【发布时间】:2012-10-05 17:12:32
【问题描述】:

我有一个 shell 脚本,我需要在其中检查两个文件是否包含相同的数据。我对很多文件都这样做了,在我的脚本中,diff 命令似乎是性能瓶颈。

这是行:

diff -q $dst $new > /dev/null

if ($status) then ...

是否有更快的方法来比较文件,也许是自定义算法而不是默认的diff

【问题讨论】:

  • 这真的很吹毛求疵,但您不是要查看两个文件是否相同,而是要询问两个文件是否具有相同的内容。相同的文件具有相同的 inode(和相同的设备)。
  • 与公认的答案不同,this answer 中的测量无法识别diffcmp 之间的任何显着差异。

标签: linux file unix diff


【解决方案1】:

我相信cmp会停在第一个字节差处:

cmp --silent $old $new || echo "files are different"

【讨论】:

  • 我怎样才能添加更多的命令而不是一个?我想复制一个文件并启动。
  • cmp -s $old $new 也可以。 -s--silent 的缩写
  • 为了提高速度,您应该在比较内容之前检查文件大小是否相等。有谁知道 cmp 是否这样做?
  • 要运行多个命令,可以使用方括号:cmp -s old new || { 不回声;呼应;回声相同; }
  • @BeowulfNode42 是的,cmp 的任何体面实现都会首先检查文件大小。这是 GNU 版本,如果您想查看它包含的其他优化:git.savannah.gnu.org/cgit/diffutils.git/tree/src/cmp.c
【解决方案2】:

我喜欢@Alex Howansky 为此使用了“cmp --silent”。但我需要正面和负面的回应,所以我使用:

cmp --silent file1 file2 && echo '### SUCCESS: Files Are Identical! ###' || echo '### WARNING: Files Are Different! ###'

然后我可以在终端中运行它或使用 ssh 来检查文件与常量文件。

【讨论】:

  • 如果您的echo success 命令(或您放置的任何其他命令)失败,您的“否定响应”命令将被运行。您应该使用“if-then-else-fi”构造。比如this simple example
【解决方案3】:

快速安全地比较任意两个文件:

if cmp --silent -- "$FILE1" "$FILE2"; then
  echo "files contents are identical"
else
  echo "files differ"
fi

它可读、高效,适用于任何文件名,包括"` $()

【讨论】:

    【解决方案4】:

    因为我很烂而且没有足够的声望点,所以我无法将这个花絮添加为评论。

    但是,如果您要使用cmp 命令(并且不需要/不想变得冗长),您可以获取退出状态。根据cmp 手册页:

    如果 FILE 为“-”或缺失,则读取标准输入。退出状态为 0 如果输入相同,则为 1,如果不同,则为 2。

    所以,你可以这样做:

    STATUS="$(cmp --silent $FILE1 $FILE2; echo $?)"  # "$?" gives exit status for each comparison
    
    if [[ $STATUS -ne 0 ]]; then  # if status isn't equal to 0, then execute code
        DO A COMMAND ON $FILE1
    else
        DO SOMETHING ELSE
    fi
    

    编辑:感谢大家的 cmets!我在这里更新了测试语法。但是,如果您正在寻找与此答案在可读性、样式和语法方面类似的内容,我建议您使用 Vasili 的答案。

    【讨论】:

    • 是的,但这实际上是更复杂的cmp --silent $FILE1 $FILE2 ; if [ "$?" == "1" ]; then echo "files differ"; fi 方法,这反过来又是更复杂的cmp --silent $FILE1 $FILE2 || echo "files differ" 方法,因为您可以直接在表达式中使用命令。它替代了$?。结果命令的存在状态将被比较。这就是另一个答案的作用。顺便提一句。如果有人正在为--silent 苦苦挣扎,那么它在所有地方都不受支持(busybox)。使用-s
    • 这可以简化为if cmp --silent -- "$FILE1" "$FILE2"; then ... else ... fi
    • 正如@VasiliNovikov 指出的那样,你也可以这样做if command; then ... else ... fi,@Gregory 你的代码有一个常见的bash 陷阱。 [[ 实际上是一种 bash 语法,它应该如下所示:if [[ ... ]](注意空格)阅读常见 bash 陷阱的一个非常好的 URL:mywiki.wooledge.org/BashPitfalls
    • @Chevraut 在重新阅读此 QA 并注意到所有当前建议并不完全安全后,我创建了自己的答案(与我在评论中写的基本相同)
    【解决方案5】:

    对于没有不同的文件,任何方法都需要完全读取这两个文件,即使读取是过去的。

    别无选择。因此,在某个时间点创建散列或校验和需要读取整个文件。大文件需要时间。

    文件元数据检索比读取大文件快得多。

    那么,您是否可以使用任何文件元数据来确定文件是否不同? 文件大小 ?甚至只是读取文件一小部分的文件命令的结果?

    文件大小示例代码片段:

      ls -l $1 $2 | 
      awk 'NR==1{a=$5} NR==2{b=$5} 
           END{val=(a==b)?0 :1; exit( val) }'
    
    [ $? -eq 0 ] && echo 'same' || echo 'different'  
    

    如果文件大小相同,那么您将无法读取完整文件。

    【讨论】:

    • 使用ls -n 来避免用户或组名有空格时出现问题。
    【解决方案6】:

    您可以通过 sha256 等校验和算法进行比较

    sha256sum oldFile > oldFile.sha256
    
    echo "$(cat oldFile.sha256) newFile" | sha256sum --check
    
    newFile: OK
    

    如果文件不同,结果将是

    newFile: FAILED
    sha256sum: WARNING: 1 computed checksum did NOT match
    

    【讨论】:

      【解决方案7】:

      使用 Raspberry Pi 3B+ 进行一些测试(我使用的是覆盖文件系统,并且需要定期同步),我对 diff -q 和 cmp -s 进行了自己的比较;请注意,这是来自 /dev/shm 内部的日志,因此磁盘访问速度不是问题:

      [root@mypi shm]# dd if=/dev/urandom of=test.file bs=1M count=100 ; time diff -q test.file test.copy && echo diff true || echo diff false ; time cmp -s test.file test.copy && echo cmp true || echo cmp false ; cp -a test.file test.copy ; time diff -q test.file test.copy && echo diff true || echo diff false; time cmp -s test.file test.copy && echo cmp true || echo cmp false
      100+0 records in
      100+0 records out
      104857600 bytes (105 MB) copied, 6.2564 s, 16.8 MB/s
      Files test.file and test.copy differ
      
      real    0m0.008s
      user    0m0.008s
      sys     0m0.000s
      diff false
      
      real    0m0.009s
      user    0m0.007s
      sys     0m0.001s
      cmp false
      cp: overwrite âtest.copyâ? y
      
      real    0m0.966s
      user    0m0.447s
      sys     0m0.518s
      diff true
      
      real    0m0.785s
      user    0m0.211s
      sys     0m0.573s
      cmp true
      [root@mypi shm]# pico /root/rwbscripts/utils/squish.sh
      

      我运行了几次。 cmp -s 在我使用的测试盒上的时间始终稍短。所以如果你想使用 cmp -s 在两个文件之间做一些事情......

      identical (){
        echo "$1" and "$2" are the same.
        echo This is a function, you can put whatever you want in here.
      }
      different () {
        echo "$1" and "$2" are different.
        echo This is a function, you can put whatever you want in here, too.
      }
      cmp -s "$FILEA" "$FILEB" && identical "$FILEA" "$FILEB" || different "$FILEA" "$FILEB"
      

      【讨论】:

        【解决方案8】:

        也尝试使用 cksum 命令:

        chk1=`cksum <file1> | awk -F" " '{print $1}'`
        chk2=`cksum <file2> | awk -F" " '{print $1}'`
        
        if [ $chk1 -eq $chk2 ]
        then
          echo "File is identical"
        else
          echo "File is not identical"
        fi
        

        cksum 命令将输出文件的字节数。请参阅“人 cksum”。

        【讨论】:

        • 这也是我的第一个想法。但是,如果您必须多次比较同一个文件,散列是有意义的,因为散列只计算一次。如果您只比较它一次,那么md5 无论如何都会读取整个文件,所以cmp,在第一个差异处停止,会更快。
        猜你喜欢
        • 1970-01-01
        • 2015-02-07
        • 2011-03-28
        • 2023-04-07
        • 1970-01-01
        • 1970-01-01
        • 2013-02-13
        • 1970-01-01
        相关资源
        最近更新 更多