在 Unix/Linux 中判断两个文件是不是具有相同内容的最快方法?

Posted

技术标签:

【中文标题】在 Unix/Linux 中判断两个文件是不是具有相同内容的最快方法?【英文标题】: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 之间的任何显着差异。 【参考方案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==1a=$5 NR==2b=$5 
       ENDval=(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,在第一个差异处停止,会更快。

以上是关于在 Unix/Linux 中判断两个文件是不是具有相同内容的最快方法?的主要内容,如果未能解决你的问题,请参考以下文章

在 unix (linux/osx) 中创建的符号链接是不是仍然可以在 Windows 中工作?

在 UNIX/Linux 中如何避免文件重叠?

UNIX/Linux系统取证之信息采集案例

我需要列出在 UNIX/Linux 中具有链接到它们的指定共享库的程序(如 ldd,反之亦然)

Unix/Linux 符号链接是不是支持回退?

在 Unix/Linux 中将文件移动到不同目录所需的权限 [关闭]