Вопрос или проблема
На Kubuntu Linux браузер Google Chrome добавляет контрольную сумму в файл, что не позволяет просто редактировать файл вручную. Поэтому я пишу скрипт для добавления контрольной суммы.
$ cat .config/google-chrome/Default/Custom\ Dictionary.txt
AATEST
dotancohen
checksum_v1 = 2b7288da7c9556608de620e65308efa4$
Нет проблем, я скопирую весь файл без последней строки и проверю, совпадает ли его MD5-хеш с этой контрольной суммой.
$ head -n -1 .config/google-chrome/Default/Custom\ Dictionary.txt > ~/chrome-dict
$ cat ~/chrome-dict
AATEST
dotancohen
$ md5sum ~/chrome-dict
2b7288da7c9556608de620e65308efa4 /home/dotancohen/chrome-dict
Мы получили 2b7288da7c9556608de620e65308efa4
, как и ожидалось. Совпадает! Так что давайте добавим это в конец файла.
$ { printf "checksum_v1 = " ; printf $(md5sum -z ~/chrome-dict | awk '{print $1}') ; } >> ~/chrome-dict
$ cat ~/chrome-dict
AATEST
dotancohen
checksum_v1 = 08f7dd79a17e12b178a1010057ef5e34$
Нет, неправильная контрольная сумма! Давайте попробуем cat, чтобы убедиться, что между двумя командами printf
ничего не записывается в файл.
$ head -n -1 .config/google-chrome/Default/Custom\ Dictionary.txt > ~/chrome-dict
$ cat ~/chrome-dict
AATEST
dotancohen
$ { printf "checksum_v1 = " ; printf $(md5sum -z ~/chrome-dict | awk '{print $1}') ; } | cat >> ~/chrome-dict
$ cat ~/chrome-dict
AATEST
dotancohen
checksum_v1 = 08f7dd79a17e12b178a1010057ef5e34$
Все еще неправильная контрольная сумма! Попробуем использовать временный файл.
$ head -n -1 .config/google-chrome/Default/Custom\ Dictionary.txt > ~/chrome-dict
$ cat ~/chrome-dict
AATEST
dotancohen
$ { printf "checksum_v1 = " ; printf $(md5sum -z ~/chrome-dict | awk '{print $1}') ; } >> ~/chrome-dict-tmp
$ cat ~/chrome-dict-tmp >> ~/chrome-dict && rm ~/chrome-dict-tmp
$ cat ~/chrome-dict
AATEST
dotancohen
checksum_v1 = 2b7288da7c9556608de620e65308efa4$
Сработало! Почему однострочные команды, которые перенаправляют вывод в конец файла ~/chrome-dict
, не возвращают правильный MD5-хеш?
Проблема: так делать нельзя:
$ { printf "checksum_v1 = " ; printf $(md5sum -z ~/chrome-dict |
awk '{print $1}') ; } >> ~/chrome-dict
как указывает @Ed, по умолчанию вы не можете одновременно читать и записывать в один и тот же файл.
Решение: вам нужен инструмент sponge
из moreutils
:
$ { printf "checksum_v1 = " ; printf $(md5sum -z ~/chrome-dict |
awk '{print $1}') ; } | sponge -a ~/chrome-dict
{ … } >> ~/chrome-dict
не агрегирует вывод из …
, чтобы отправить его в файл после завершения всех операций, он просто заставляет команды внутри унаследовать тот же файловый дескриптор. Ваш md5sum
работает после первого printf
, и поэтому он может читать файл с уже добавленным текстом.
Запустите md5sum
перед всем, что добавляет данные в файл. Это легко сделать, когда вы выполняете всю печать с помощью одного printf
:
printf 'checksum_v1 = %s' "$(md5sum ~/chrome-dict | awk '{print $1}')" >> ~/chrome-dict
Теперь все внутри $()
должно завершиться перед тем, как начнется printf
, поэтому нет риска, что printf
добавит данные в файл раньше, чем md5sum
прочтет его полностью. В исходном коде только второй printf
был “заблокирован” этим.
Ваш awk
может хорошо работать с неполной строкой и завершающим нулевым байтом, но в общем случае лучше предоставлять текстовым инструментам правильный текст; поэтому я удалил -z
при вызове md5sum
. В нашем случае, если awk
работает при любом варианте, то print $1
в нем будет работать так же, и $()
в любом случае удалит конечный перевод строки. Но если бы реализация awk
строго требовала текст, то -z
могло бы просто все сломать.
.
Ответ или решение
Почему файл изменяется перед записью?
На Kubuntu Linux при использовании браузера Google Chrome добавляется контрольная сумма в файл, предотвращая его ручное редактирование. В приведённой ситуации обсуждается проблема с добавлением контрольной суммы к файлу словаря Google Chrome, из-за чего при попытке записи в файл изменяется его содержимое и контрольная сумма не совпадает.
Проблема
Суть проблемы заключается в том, что нельзя одновременно читать из и записывать в один и тот же файл. Ваша команда { … } >> ~/chrome-dict
передает вывод команды напрямую в файл, инициируя запись в него, прежде чем завершится чтение для вычисления контрольной суммы с помощью md5sum
.
Таким образом, когда вы запрашиваете md5sum
для вычисления контрольной суммы файла, он уже видит изменённое состояние файла с добавленной строкой контрольной суммы, хотя вы рассчитываете на его исходное состояние.
Решение
Использование sponge
из пакета moreutils
позволит избежать проблемы одновременного чтения и записи. За счет буферизации вывода, этот инструмент гарантирует, что запись в файл произойдёт лишь после завершения всех действий по его чтению. Вот как можно решить проблему с помощью sponge
:
{ printf "checksum_v1 = " ; printf "$(md5sum ~/chrome-dict | awk '{print $1}')" ; } | sponge -a ~/chrome-dict
Объяснение техники
-
Отложенная запись:
sponge
временно буферизует данные, полученные из команд, и запишет их в файл только после завершения всех операций. -
Целостность данных: Это гарантирует, что в момент вычисления контрольной суммы файл находится в изначальном состоянии, что позволяет корректно рассчитать и добавить контрольную сумму.
-
Упрощение выполнения: Использование
printf
для объединения всех операций в одну команду обеспечивает выполнениеmd5sum
до начала любой записи в файл. Таким образом, использование однократного вызоваprintf
минимизирует вероятность возникновения ошибок.
Выводы
Понимание того, как UNIX-like системы обрабатывают файловые дескрипторы, критично для корректного управления данными. Использование инструментов, таких как sponge
, эффективно решает проблемы конкурентного доступа к файловой системе и позволяет автоматизировать задачи более надежным образом.
Этот профессиональный подход поможет в устранении ошибок и увеличении надежности долгосрочных решений в области управления файлами в операционных системах Linux.