Ответов (13)13
Я нашел облегчение в этом простом скрипте Perl: diffzips.pl
Он рекурсивно сравнивает каждый zip-файл внутри исходного zip, что особенно полезно для разных форматов пакетов Java: jar, war и ear.
zipcmp использует более простой подход и не рекурсивно превращается в архивные zip-файлы .
На самом деле gzip и bzip2 поставляются со специальными инструментами для этого.
С помощью gzip:
$ zdiff file1.gz file2.gz
С bzip2:
$ bzdiff file1.bz2 file2.bz2
Но имейте в виду, что для очень больших файлов вы можете столкнуться с проблемами памяти (изначально я пришел сюда, чтобы узнать, как их решить, поэтому у меня пока нет ответа).
Я обычно использую подход, подобный @ mrabbit, но запускаю 2 команды распаковки и при необходимости различаю вывод. Например, мне нужно сравнить 2 файла WAR Java.
$ sdiff --width 160 \
<(unzip -l -v my_num1.war | cut -c 1-9,59-,49-57 | sort -k3) \
<(unzip -l -v my_num2.war | cut -c 1-9,59-,49-57 | sort -k3)
Результат такой:
-------- ------- -------- -------
Archive: Archive:
-------- -------- ---- -------- -------- ----
48619281 130 files | 51043693 130 files
1116 060ccc56 index.jsp 1116 060ccc56 index.jsp
0 00000000 META-INF/ 0 00000000 META-INF/
155 b50f41aa META-INF/MANIFEST.MF | 155 701f1623 META-INF/MANIFEST.MF
Length CRC-32 Name Length CRC-32 Name
1179 b42096f1 version.jsp 1179 b42096f1 version.jsp
0 00000000 WEB-INF/ 0 00000000 WEB-INF/
0 00000000 WEB-INF/classes/ 0 00000000 WEB-INF/classes/
0 00000000 WEB-INF/classes/com/ 0 00000000 WEB-INF/classes/com/
...
...
Я отказался от попыток использовать существующие инструменты и написал небольшой скрипт на bash, который мне подходит:
#!/bin/bash
# Author: Onno Benschop, [email protected]
# Note: This requires enough space for both archives to be extracted in the tempdir
if [ $# -ne 2 ] ; then
echo Usage: $(basename "$0") zip1 zip2
exit
fi
# Make temporary directories
archive_1=$(mktemp -d)
archive_2=$(mktemp -d)
# Unzip the archives
unzip -qqd"${archive_1}" "$1"
unzip -qqd"${archive_2}" "$2"
# Compare them
diff -r "${archive_1}" "${archive_2}"
# Remove the temporary directories
rm -rf "${archive_1}" "${archive_2}"
Решение на Python для zip-файлов:
import difflib
import zipfile
def diff(filename1, filename2):
differs = False
z1 = zipfile.ZipFile(open(filename1))
z2 = zipfile.ZipFile(open(filename2))
if len(z1.infolist()) != len(z2.infolist()):
print "number of archive elements differ: {} in {} vs {} in {}".format(
len(z1.infolist()), z1.filename, len(z2.infolist()), z2.filename)
return 1
for zipentry in z1.infolist():
if zipentry.filename not in z2.namelist():
print "no file named {} found in {}".format(zipentry.filename,
z2.filename)
differs = True
else:
diff = difflib.ndiff(z1.open(zipentry.filename),
z2.open(zipentry.filename))
delta = ''.join(x[2:] for x in diff
if x.startswith('- ') or x.startswith('+ '))
if delta:
differs = True
print "content for {} differs:\n{}".format(
zipentry.filename, delta)
if not differs:
print "all files are the same"
return 0
return 1
Использовать как
diff(filename1, filename2)
Он построчно сравнивает файлы в памяти и показывает изменения.
Многие решения здесь либо просто проверяют CRC, чтобы увидеть, существуют ли различия , являются ли сложными сценариями, требуют распаковки на диск, используют внешние программы или нуждаются в определенных форматах сжатия, отличных от того, о котором вы спрашивали ( zcat НЕ работает с zip ).
Вот простой, легкий для чтения и должен работать везде, где у вас есть bash, который показывает различия между содержимым файла, если, как и я, это то, что вам нужно, когда вы столкнулись с этим вопросом :
diff \
<(zipinfo -1 "$zip1" '*' \
| grep '[^/]$' \
| sort \
| while IFS= read -r file; do unzip -c "$zip1" "$file"; done \
) \
<(zipinfo -1 "$zip2" '*' \
| grep '[^/]$' \
| sort \
| while IFS= read -r file; do unzip -c "$zip2" "$file"; done \
)
Это распаковывает в памяти, а не на диск, высвобождая данные из канала по мере их разницы (он не будет распаковывать, а затем сравнивать, поэтому не должен использовать много памяти).
Хотите изменить параметры различий для игнорирования пробелов или использования бок о бок? Измените diff
на diff -w
или gvimdiff
(это сохранит все файлы в памяти) и так далее.
Скажете, вы хотите только сравнить .js
файлы? Измените *
на *.js
.
Хотите видеть только те имена файлов, которые отсутствуют в одном или другом? Удалите while
линию, и она не будет распаковывать.
Легкий.
Он даже будет безопасно обрабатывать (пропускать и записывать stderr
) имена файлов с «недопустимыми» символами, такими как перевод строки и обратная косая черта.
Нет "безопасного" р, чем это.
Ответ slm довольно хорош для возврата файлов, которые отличаются (без отображения различий) и даже не распаковываются, что приятно. Если по какой-то причине вы хотите этого, но на шаг выше CRC, в этом ответе вы можете добавить, например, | sha512sum
перед, ; done
и получить `` худшее из обоих миров '': P
Точно так же сравнительно легко сравнить архив и реальный каталог:
diff \
<(zipinfo -1 "$zip" '*' \
| grep '[^/]$' \
| sort \
| while IFS= read -r file; do unzip -c "$zip" "$file"; done \
) \
<(find "$directory" -type f -name '*' \
| sort \
| while IFS= read -r file
do
printf 'Archive: %s\n inflating: %s\n' "$directory" `echo $file | sed "s|$directory/||"`
cat "$file"
echo
done \
)
Или, игнорируя файлы только в каталоге, в основном удобный пробный прогон unzip -o -d "$directory"
:
diff \
<(zipinfo -1 "$zip" '*' \
| grep '[^/]$' \
| sort \
| while IFS= read -r file; do unzip -c "$zip" "$file"; done \
) \
<(zipinfo -1 "$zip" '*' \
| grep '[^/]$' \
| sort \
| while IFS= read -r file
do
printf 'Archive: %s\n inflating: %s\n' "$directory" "$file"
cat "$directory/$file"
echo
done \
)
Windows? Извините. Хотя сценарии просты и их будет легко перенести на [синтаксически] фантастическую оболочку PowerShell, это не сработает. Собственный командлет только извлекает данные на диск, а MS до сих пор не исправила сломанный конвейер двоичных данных в PS, поэтому вы также не можете «безопасно» использовать внешние данные zip.exe
таким образом.
Очевидно, что другие делали аналогичные вещи, используя .NET API напрямую , но он стал бы менее элегантным портом и больше переопределением в .NET: |
Замечание о «незаконных именах файлов», упомянутых ранее:
если вы хотите, чтобы он работал с ними, на самом деле это не так уж сложно; вам просто нужно поменять местами $file
с $(echo "$file" | sed 's/\\/\\\\/g;s/\^J/\n/g;s/\^M/\r/g')
.
Добавляйте другие символы ctrl по мере их появления.
Причина в том, что по какой-то причине, даже если zipinfo
отображается имя файла, содержащее \n
в нем как ^J
, он не будет принимать эти безопасные имена unzip
только для оригинала! И даже несмотря на то, что он МОЖЕТ извлекать эти незаконные имена файлов unzip -^
, нет никакого способа получить эти исходные имена файлов zipinfo
вообще. Таким образом, вам нужно создать исходное недопустимое имя файла из безопасного, непригодного для использования, чтобы ссылаться на них для сравнения :(
Если вы это сделаете, обратите внимание, что нет способа отличить ^J
буквально от \n
отображения как ^J
, и этот zip не поддерживает /
или ^@
вообще внутри имен файлов.
В качестве бонуса; вы можете записать все эти различия прямо в архив и хранить их все в иерархии папок, совпадающей с исходными файлами, вместо того, чтобы пытаться прочитать все сразу одним большим знаком.
(zipinfo -1 "$zip1"; zipinfo -1 "$zip2") \
| grep '[^/]$' \
| sort \
| uniq \
| while IFS= read -r file; do
(diff <(unzip -p "$zip1" "$file") <(unzip -p "$zip2" "$file") | zip 'diff.zip' - \
&& zipinfo -s 'diff.zip' - | awk '{ print $4; }' | grep '[^0]' \
&& printf "@ -\[email protected]=$file\n" | zipnote -w 'diff.zip' \
|| zip -d 'diff.zip' -
) >/dev/null
done
Не такой красивый сценарий, но теперь вы можете открыть его в выбранном вами архиваторе графического интерфейса или сделать, unzip -p diff.zip some/dir/some.file
чтобы увидеть различия конкретно с этим файлом, или получить ответ «не найден», если нет различий, что на практике намного красивее. .
У Beyond compare нет проблем с этим.
В общем, вы не можете избежать распаковки и последующего сравнения. Различные компрессоры будут приводить к разным байтовым потокам DEFLATEd, которые, когда INFLATEd приводят к одному и тому же исходному тексту. Вы не можете просто сравнить данные DEFLATEd друг с другом. В некоторых случаях это НЕ ИСПОЛЬЗУЕТСЯ.
Но в сценарии ZIP для каждой записи вычисляется и сохраняется CRC32. Поэтому, если вы хотите проверить файлы, вы можете просто сравнить сохраненный CRC32, связанный с каждым потоком DEFLATEd, с предостережениями в отношении свойств уникальности хэша CRC32. Возможно, вам понадобится сравнить FileName и CRC.
Вам понадобится ZIP-библиотека, которая читает zip-файлы и предоставляет эти вещи как свойства объекта «ZipEntry». DotNetZip сделает это для приложений .NET.
Это не особенно элегантно, но вы можете использовать приложение FileMerge, которое поставляется с инструментами разработчика Mac OS X, для сравнения содержимого zip-файлов с помощью настраиваемого фильтра.
Создайте скрипт ~/bin/zip_filemerge_filter.bash
с содержимым:
#!/bin/bash
##
# List the size, CR-32 checksum, and file path of each file in a zip archive,
# sorted in order by file path.
##
unzip -v -l "${1}" | cut -c 1-9,59-,49-57 | sort -k3
exit $?
Сделайте скрипт исполняемым ( chmod +x ~/bin/zip_filemerge_filter.bash
).
Откройте FileMerge, откройте «Настройки» и перейдите на вкладку «Фильтры». Добавьте элемент в список с помощью: Extension: "zip", Filter: "~ / bin / zip_filemerge_filter.bash $ (FILE)", Display: Filtered, Apply *: No. (Я также добавил фильтр для .jar и файлы .war.)
Затем используйте FileMerge (или оболочку командной строки opendiff) для сравнения двух файлов .zip.
Это не позволит вам различать содержимое файлов в zip-архивах, но позволит вам быстро увидеть, какие файлы появляются в одном архиве, а какие файлы существуют в обоих, но имеют разное содержимое (т.е. разный размер и / или контрольную сумму).