在 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 中的测量无法识别diff
和cmp
之间的任何显着差异。
【参考方案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 中工作?