Почему файл изменяется до того, как в него записывают данные?

Вопрос или проблема

На 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

Объяснение техники

  1. Отложенная запись: sponge временно буферизует данные, полученные из команд, и запишет их в файл только после завершения всех операций.

  2. Целостность данных: Это гарантирует, что в момент вычисления контрольной суммы файл находится в изначальном состоянии, что позволяет корректно рассчитать и добавить контрольную сумму.

  3. Упрощение выполнения: Использование printf для объединения всех операций в одну команду обеспечивает выполнение md5sum до начала любой записи в файл. Таким образом, использование однократного вызова printf минимизирует вероятность возникновения ошибок.

Выводы

Понимание того, как UNIX-like системы обрабатывают файловые дескрипторы, критично для корректного управления данными. Использование инструментов, таких как sponge, эффективно решает проблемы конкурентного доступа к файловой системе и позволяет автоматизировать задачи более надежным образом.

Этот профессиональный подход поможет в устранении ошибок и увеличении надежности долгосрочных решений в области управления файлами в операционных системах Linux.

Оцените материал
Добавить комментарий

Капча загружается...