4

У меня есть несколько огромных файлов, которые в настоящее время разархивированы, и я бы хотел их сохранить. Я хочу настроить скрипт для этого, но я хочу быть осторожным, чтобы не потерять данные, то есть я никогда не должен удалять версию gzipped, если версия xz не была определенно создана правильно. Поскольку это большие файлы, я бы также предпочел не разархивировать файл на диск. Я думал, что труба set -o pipefail; gzip -dc file.gz | xz > file.xz && rm file.gz может быть близко к тому, что я хочу. Какой правильный способ сделать это? Гарантируется ли это перехват всех сбоев, произошедших до удаления окончательного файла?

1 ответ1

9

Добавление суммы SHA1 (которая математически гарантирует до невероятно высокой степени уверенности в том, что файлы либо совпадают, когда совпадают хэши, и хэши не совпадают, когда файлы не совпадают), добавляет меру целостности данных для защиты от случаев, когда дисковая подсистема могла сделать (тихую) ошибку во время записи. Безмолвная коррупция редка, но коварна, когда это происходит.

Конечно, вы все равно могли бы получить ошибочные результаты, если у вас возникли случайные ошибки при чтении, но в этом случае суммы не будут совпадать в любом случае, с чрезвычайно высокой степенью достоверности. Другими словами, если система повреждена (либо ОЗУ, либо диск выдает неправильные биты / перевернутые биты / поврежденные данные), то произойдет сбой, когда простой && может преуспеть, и вероятность попадания в строку rm с поврежденными данными они исчезающе малы (поскольку большинство ошибок, как правило, повреждают данные случайным образом, шансы случайного изменения, вызывающего столкновение хеша в SHA1 во время чтения, невероятно крошечные).

#!/bin/bash
set -e
set -o pipefail
ORIGSUM=$(gzip -dc file.gz | tee >(xz > file.xz) | sha1sum)
NEWSUM=$(unxz -c file.xz | sha1sum)
if [ "${ORIGSUM}" = "${NEWSUM}" ]; then rm file.gz; fi

set -e заставляет скрипт оболочки завершиться, как только любая строка скрипта возвращает ненулевой код выхода.

Затем мы используем команду tee чтобы скопировать распакованный вывод файла как в компрессор xz , так и в программу sha1sum . sha1sum вычисляет сумму SHA1 исходных данных, содержащихся в архиве gzip, временно распаковывая их в программу sha1sum, которая считывает данные для вычисления суммы и затем отбрасывает данные. Используя tee , нам нужно только заплатить ЦП за разархивирование файла один раз.

Затем мы выполняем дополнительный вычислительно-дорогой шаг (для супер-дополнительной проверки) и удаляем сжатие xz для файла (временно, в поток) и направляем его в sha1sum, чтобы получить нашу сумму SHA1 "нового файла".

Затем мы сравниваем две суммы, и если они не равны строки, или если одна или обе из них имеют нулевую длину, мы либо получим ошибку сценария (которая завершается благодаря set -e), либо файл выиграл не быть удаленным. Вы можете реализовать предложение else для удобной обработки ошибок, если хотите, но этот важный сценарий как есть будет чрезвычайно безопасным, хотя и не очень информативным для пользователя, выполняющего команду в интерактивном режиме.

В конце концов, file.gz будет разорван только в том случае, если несжатое содержимое file.gz и file.xz будет точно идентичным в момент времени, когда были вычислены хеши, с астрономически высокой степенью достоверности ( вероятность того, что что-то пойдет не так, будет примерно 1 к 1 с 300 нулями после этого). На этом этапе вам нужно беспокоиться только о повреждении данных после завершения работы этого скрипта. ;)


Спектакль

Этот сценарий будет работать почти с той же скоростью, что и исходный сценарий в вопросе, за исключением части, которая запускает unxz . К счастью, распаковка из LZMA происходит чрезвычайно быстро, почти так же быстро, как обычная Zip, и примерно на порядок быстрее, чем сжатие в LZMA. Если у вас есть быстрый процессор, а файлы достаточно малы, это не следует добавлять слишком много времени выполнения для сценария, но если вы дорожите целостность данных по производительности, это чистая победа.


Кредит, где кредит должен

Этот ответ на StackOverflow помог мне существенно написать этот скрипт.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .