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


231

我有一个Shell脚本,在其中需要检查两个文件是否包含相同的数据。我对大量文件执行此操作,并且在我的脚本中,该diff命令似乎是性能瓶颈。

这是一行:

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

if ($status) then ...

有没有一种比较快速的方法来比较文件,也许是自定义算法而不是默认算法diff


10
这确实很挑剔,但您并不是要问两个文件是否相同,而是要问两个文件是否具有相同的内容。相同的文件具有相同的inode(和相同的设备)。
扎诺2014年

1
与接受的答案不同,此答案中的度量值无法识别diff和之间的任何显着差异cmp
wedi

Answers:


388

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

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

1
如何添加多于一个的命令?我想复制一个文件并启动。
feedc0de 2014年

9
cmp -s $old $new也可以。-s的缩写--silent
Rohmer

7
为了提高速度,在比较内容之前,应检查文件大小是否相等。有人知道cmp是否这样做吗?
BeowulfNode42

3
要运行多个命令,可以使用方括号:cmp -s old new || {不回声;回声 回声一样 }
unfa

6
@ BeowulfNode42是的,任何不错的实现都cmp将首先检查文件大小。这是GNU版本,如果您想查看它包括的其他优化:git.savannah.gnu.org/cgit/diffutils.git/tree/src/cmp.c
Ryan Graham

53

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

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

然后,我可以在终端中或使用ssh来运行此程序,以对照常量文件检查文件。


16
如果您的echo success命令(或放置在其上的任何其他命令)失败,则将运行“负响应”命令。您应该使用“ if-then-else-fi”构造。例如,像这个简单的例子
通配符

18

为什么不同时获取两个文件内容的哈希值?

尝试此脚本,将其称为例如script.sh,然后按如下所示运行它:script.sh file1.txt file2.txt

#!/bin/bash

file1=`md5 $1`
file2=`md5 $2`

if [ "$file1" = "$file2" ]
then
    echo "Files have the same content"
else
    echo "Files have NOT the same content"
fi

2
@THISUSERNEEDSHELP这是因为哈希算法不是一对一的。设计它们的目的是使散列空间很大,并且不同的输入很有可能产生不同的哈希。但是现实是,哈希空间是有限的,而可能要哈希的文件范围不是-最终您将发生冲突。在密码学中,这被称为“ 生日袭击”

5
@will Eh,可以有效保证它能正常工作。从数学上讲,它不起作用的几率是大约1/(2^511)。除非您担心有人故意创建碰撞,否则此方法产生假阳性的想法并不是真正的问题。cmp不过,它仍然更有效,因为在文件不匹配的情况下不必读取整个文件。
Ajedi32

12
OP要求以最快的方式...(如果不匹配),搜索第一个不匹配的位(使用cmp)是否比散列整个文件的速度更快(特别是如果文件很大)?
KoZm0kNoT '16

3
如果您要进行一对多比较,则md5最好。您可以将md5哈希存储为属性或针对每个文件存储在数据库中。如果出现一个新文件,并且您必须检查文件系统上任何位置是否存在相同文件,那么您要做的就是计算新文件的哈希值,并对照所有先前文件进行检查。我确定Git在提交期间使用哈希来检查文件更改,但是他们使用SHA1。
JimHough,

3
@ BeowulfNode42这就是为什么我在我的评论开头加上“除非您担心有人故意制造碰撞”
Ajedi32 '17

5

因为我很烂并且没有足够的声誉点,所以我无法在评论中添加此花絮。

但是,如果您要使用该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

是的,但这实际上是更复杂的方法,cmp --silent $FILE1 $FILE2 ; if [ "$?" == "1" ]; then echo "files differ"; fi而反过来又是更复杂的方法,cmp --silent $FILE1 $FILE2 || echo "files differ"因为您可以直接在表达式中使用命令。它代替$?。结果,将比较命令的存在状态。这就是其他答案。顺便说一句 如果有人在苦苦挣扎--silent,则并非到处都支持它(busybox)。使用-s
papo

4

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

没有替代。因此,在某个时间点创建哈希或校验和需要读取整个文件。大文件需要时间。

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

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

文件大小示例代码片段:

  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'  

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


1
使用ls -n到,如果用户或组名避免出现问题有空格。
tricasse

2

也尝试使用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命令将输出文件的字节数。参见“ man cksum”。


2
那也是我的第一个想法。但是,如果您必须多次比较同一文件,则散列是有意义的,因为散列仅计算一次。如果您只比较一次,那么md5无论如何都会读取整个文件,因此cmp,在第一个差异处停止会更快。
Francesco Dondi

0

用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"
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.