Вопрос или проблема
Если я попробую truncate -s 0 log.log
(:>log.log
имеет то же поведение), отображаемое пространство на диске становится свободным, но размер файла (ls -l
) остается тем же (хотя du
показывает меньше). Насколько я понимаю, это происходит, потому что указатель все еще “старый”.
Это поведение приводит к тому, что я не могу использовать команду cat ... | grep ...
: CLI говорит, что файл является двоичным. Так что единственный способ – использовать less
или другие команды.
Итак, как я могу обрезать файл, который открыт в режиме записи в другом процессе и иметь правильный размер файла после обрезки? Мне нужно, чтобы данные в log.log
были обрезаны в другой файл или просто удалить все данные в файле, не удаляя сам файл.
Вы не можете. Указатель “следующего блока для записи” хранится в памяти процесса, который открыл файл для записи. Вы не можете получить к нему доступ извне.
Некоторые программы были написаны, чтобы закрывать старые журналы и открывать новые при получении сигнала.
Простое удаление файла, когда программа собирается записать блок 1024, приведет к 1023 блокам нулей и данным в блоке 1024.
Ответ waltinator правильный, но вот некоторые альтернативы, которые могут быть достаточно хороши:
-
Если у вас есть контроль над процессом, записывающим в
log.log
, откройте его в режиме добавления (например,>>
в некоторых версиях bash и zsh или режим'a'
в функцииopen
на Python). Тогда он будет переходить в конец файла перед каждой записью — таким образом учитывая обрезки.Учтите, что зависит от файловой системы, выполняются ли переход к концу и запись как одна атомарная операция, — поэтому в частности вы можете зависеть или не зависеть от этого для разрешения конфликтов между несколькими записывающими. (Поиск
O_APPEND
вman 2 open
для получения полной информации.)Источник: старая запись Джонатана Лефлера.
-
Если вы просто хотите пропустить нулевые байты при передаче в вашу систему анализа журналов, сделайте одно из следующего:
sed -e '1s/^\x0*//' log.log | grep ... # пропускает начальные нулевые байты tr -d '\0' < log.log | grep ... # пропускает все нулевые байты
-
Если вы пытаетесь освободить место на диске, выполните
truncate -s 0 log.log
. Вероятно, вы находитесь на файловой системе, поддерживающей разреженные файлы, и в этом случае полностью пустые блоки не будут храниться. Проверьтеdf
до и после, чтобы увидеть, сработало ли это.Помните, что файл, который удаляется вместо этого, продолжит существовать и занимать место, пока все его не закроют. Python даже делает это намеренно — его TemporaryFile удаляется перед началом записи, чтобы быть уверенным, что он не останется после в случае ненормального завершения.
Ответ или решение
Трудности, связанные с изменением или обнулением файлов, которые в настоящий момент открыты в другом процессе, могут вызвать много вопросов у специалистов по информационным технологиям. В основе данной проблемы лежит то, что в операционных системах, как правило, отсутствуют встроенные механизмы для внешнего доступа к управляющим указателям процесса, который пишет в файл. Это создает определенные ограничения и требует поиска альтернативных решений.
Теория
Проблема обнуления файла, который в настоящий момент открыт в другом процессе, связана с тем, как операционная система и программы управляют файлами. Когда файл открыт для записи, программа хранит в своей памяти указатель, где будет записываться следующая информация. Этот указатель не зависит от фактического состояния файла на диске. Поэтому изменение самого файла из-под другой программы (например, удаление его содержимого при помощи truncate
) может не изменить указатель на следующую запись в процессе, который изначально открыл файл, что приводит к некорректному поведению.
Пример
Допустим, у нас есть лог-файл log.log
, который активно используется некоторым сервисом для записи логов. При попытке обнулить этот файл вне процесса, занимавшегося его записью (truncate -s 0 log.log
), возникает ситуация, в которой фактически часть дискового пространства освобождается, но процесс, продолжающий использовать файл, сохраняет изначальное положение указателя для записи. Это приводит к разрозненности данных и неправильно отображается размер файла.
Применение
Существуют разнообразные стратегии, которые можно рассмотреть в этой ситуации:
-
Использование режима добавления (append mode): Если у вас есть контроль над процессом, который пишет в
log.log
, рекомендуется открыть файл в режиме добавления, например, через>>
в bash или в режиме'a'
в Python. Это значает, что каждый раз перед записью программа будет автоматически перемещать указатель в конец файла, что гарантирует значительное упрощение при выполнении операций, связанных с обнулением файла. -
Использование служб и сигналов для обращения с файлами: Некоторые приложения могут быть настроены на приём сигналов от системы (например, SIGHUP в Unix-системах), которые указывают программе закрыть текущий лог-файл и открыть новый. Это может быть полезной практикой для обеспечения новых чистых записей и чтобы избежать каких-либо затруднений, связанных с размерностью файлов.
-
Удаление и управление нулевыми байтами: Если необходимо лишь освободить дисковое пространство, можно продолжать использовать
truncate
. В случае, если обработка файла порождает проблемы с бинарными данными или нулями в выводе, возможно, придётся дополнительно очистить такие данные через фильтрацию, используя команды, такие какtr -d '\0'
илиsed -e '1s/^\x0*//'
. -
Управление пространством данных: Важно понимать, что удаление файла, в который продолжается запись, не освободит дисковое пространство, пока файл не будет закрыт всеми его процессами. В основе этой логики лежит работа файловых систем, например, временные файлы в Python функционируют именно так, чтобы защищать от утечек даже при аномальном завершении.
В процессе управления лог-файлами, открытыми в других процессах, ключевым является грамотное управление файлами на уровне приложений и операционной системы, а также использование потенциальных возможностей, заложенных в самом приложении. Каждое решение должно тщательно учитываться в свете требований конкретной системы и задач.