Самый быстрый способ узнать, совпадают ли два файла в Unix / Linux?

156

У меня есть оболочка script, в которой мне нужно проверить, совпадают ли два файла или нет. Я делаю это для большого количества файлов, и в моей script команда diff кажется узким местом производительности.

Здесь строка:

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

if ($status) then ...

Может ли быть более быстрый способ сравнения файлов, может быть, пользовательский алгоритм вместо стандартного diff?

  • 7
    Это действительно придирчиво, но вы не спрашиваете, являются ли два файла одинаковыми, вы спрашиваете, имеют ли два файла одинаковое содержимое. Одинаковые файлы имеют одинаковые inode (и одно и то же устройство).
  • 1
    В отличие от принятого ответа, измерение в этом ответе не распознает заметных различий между diff и cmp .
Теги:
file
diff

7 ответов

274
Лучший ответ

Я полагаю, что cmp остановится при разнице первого байта:

cmp --silent $old $new || echo "files are different"
  • 1
    Как я могу добавить больше команд, чем только одну? Я хочу скопировать файл и перезагрузиться.
  • 0
    @DanielBrunner: вы можете скопировать из стандартного ввода в файл и стандартный вывод с помощью команды tee .
Показать ещё 8 комментариев
37

Мне нравится @Alex Howansky использовал для этого 'cmp --silent'. Но мне нужен как положительный, так и отрицательный ответ, поэтому я использую:

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

Затем я могу запустить это в терминале или с помощью ssh для проверки файлов с постоянным файлом.

  • 12
    Если ваша команда echo success (или любая другая команда, которую вы положили на ее место) терпит неудачу, будет запущена ваша команда «отрицательного ответа». Вы должны использовать конструкцию if-then-else-fi. Например, как этот простой пример .
  • 0
    заменить, пожалуйста, последний " в сценарии
16

Почему бы вам не получить хэш обоих файлов?

Попробуйте этот script, вызовите его, например, 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
  • 0
    извините, не уверен, что вы имеете в виду, я не слишком долго писал сценарии Unix.
  • 0
    Если два файла одинаковы, то они будут иметь одинаковое значение хеш-функции. Например, если 'file1.txt' содержит 'aaa' и 'file2.txt' так, то когда вы получите хэш md5: md5 file1.txt, вы получите: 5c9597f3c8245907ea71a89d9d39d08e, который будет таким же, как и md5 file2.txt, если вы обрабатываете оба хэша, и они одинаковы, вы можете быть уверены, что они оба имеют одинаковое содержание :)
Показать ещё 21 комментарий
4

Для файлов, которые не отличаются друг от друга, любой метод требует полного чтения обоих файлов, даже если чтение было в прошлом.

Альтернативы нет. Поэтому создание хэшей или контрольных сумм в определенный момент времени требует чтения всего файла. Большие файлы требуют времени.

Поиск метаданных файлов намного быстрее, чем чтение большого файла.

Итак, есть ли какие-либо метаданные файлов, которые вы можете использовать, чтобы установить, что файлы разные? Размер файла? или даже результаты команды файла, которая только что прочитала небольшую часть файла?

Пример фрагмента кода размера файла:

  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 чтобы избежать проблем, если имена пользователей или групп имеют пробелы.
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 при первом разнице будет намного быстрее.
0

Поскольку я сосать и не хватает очков репутации, я не могу добавить этот лакомый кусочек в качестве комментария.

Но, если вы собираетесь использовать команду cmp (и вам не нужно/хотите быть подробным), вы можете просто захватить статус выхода. На странице man 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
0

Выполняя некоторые тесты с помощью малины 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"

Ещё вопросы

Сообщество Overcoder
Наверх
Меню