Обрезать файл, который открыт в другом процессе

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

Если я попробую truncate -s 0 log.log (:>log.log имеет то же поведение), отображаемое пространство на диске становится свободным, но размер файла (ls -l) остается тем же (хотя du показывает меньше). Насколько я понимаю, это происходит, потому что указатель все еще “старый”.

Это поведение приводит к тому, что я не могу использовать команду cat ... | grep ...: CLI говорит, что файл является двоичным. Так что единственный способ – использовать less или другие команды.

Итак, как я могу обрезать файл, который открыт в режиме записи в другом процессе и иметь правильный размер файла после обрезки? Мне нужно, чтобы данные в log.log были обрезаны в другой файл или просто удалить все данные в файле, не удаляя сам файл.

Вы не можете. Указатель “следующего блока для записи” хранится в памяти процесса, который открыл файл для записи. Вы не можете получить к нему доступ извне.

Некоторые программы были написаны, чтобы закрывать старые журналы и открывать новые при получении сигнала.

Простое удаление файла, когда программа собирается записать блок 1024, приведет к 1023 блокам нулей и данным в блоке 1024.

Ответ waltinator правильный, но вот некоторые альтернативы, которые могут быть достаточно хороши:

  1. Если у вас есть контроль над процессом, записывающим в log.log, откройте его в режиме добавления (например, >> в некоторых версиях bash и zsh или режим 'a' в функции open на Python). Тогда он будет переходить в конец файла перед каждой записью — таким образом учитывая обрезки.

    Учтите, что зависит от файловой системы, выполняются ли переход к концу и запись как одна атомарная операция, — поэтому в частности вы можете зависеть или не зависеть от этого для разрешения конфликтов между несколькими записывающими. (Поиск O_APPEND в man 2 open для получения полной информации.)

    Источник: старая запись Джонатана Лефлера.

  2. Если вы просто хотите пропустить нулевые байты при передаче в вашу систему анализа журналов, сделайте одно из следующего:

    sed -e '1s/^\x0*//' log.log | grep ...  # пропускает начальные нулевые байты
    tr -d '\0' < log.log | grep ...  # пропускает все нулевые байты
    
  3. Если вы пытаетесь освободить место на диске, выполните truncate -s 0 log.log. Вероятно, вы находитесь на файловой системе, поддерживающей разреженные файлы, и в этом случае полностью пустые блоки не будут храниться. Проверьте df до и после, чтобы увидеть, сработало ли это.

    Помните, что файл, который удаляется вместо этого, продолжит существовать и занимать место, пока все его не закроют. Python даже делает это намеренно — его TemporaryFile удаляется перед началом записи, чтобы быть уверенным, что он не останется после в случае ненормального завершения.

Ответ или решение

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

Теория

Проблема обнуления файла, который в настоящий момент открыт в другом процессе, связана с тем, как операционная система и программы управляют файлами. Когда файл открыт для записи, программа хранит в своей памяти указатель, где будет записываться следующая информация. Этот указатель не зависит от фактического состояния файла на диске. Поэтому изменение самого файла из-под другой программы (например, удаление его содержимого при помощи truncate) может не изменить указатель на следующую запись в процессе, который изначально открыл файл, что приводит к некорректному поведению.

Пример

Допустим, у нас есть лог-файл log.log, который активно используется некоторым сервисом для записи логов. При попытке обнулить этот файл вне процесса, занимавшегося его записью (truncate -s 0 log.log), возникает ситуация, в которой фактически часть дискового пространства освобождается, но процесс, продолжающий использовать файл, сохраняет изначальное положение указателя для записи. Это приводит к разрозненности данных и неправильно отображается размер файла.

Применение

Существуют разнообразные стратегии, которые можно рассмотреть в этой ситуации:

  1. Использование режима добавления (append mode): Если у вас есть контроль над процессом, который пишет в log.log, рекомендуется открыть файл в режиме добавления, например, через >> в bash или в режиме 'a' в Python. Это значает, что каждый раз перед записью программа будет автоматически перемещать указатель в конец файла, что гарантирует значительное упрощение при выполнении операций, связанных с обнулением файла.

  2. Использование служб и сигналов для обращения с файлами: Некоторые приложения могут быть настроены на приём сигналов от системы (например, SIGHUP в Unix-системах), которые указывают программе закрыть текущий лог-файл и открыть новый. Это может быть полезной практикой для обеспечения новых чистых записей и чтобы избежать каких-либо затруднений, связанных с размерностью файлов.

  3. Удаление и управление нулевыми байтами: Если необходимо лишь освободить дисковое пространство, можно продолжать использовать truncate. В случае, если обработка файла порождает проблемы с бинарными данными или нулями в выводе, возможно, придётся дополнительно очистить такие данные через фильтрацию, используя команды, такие как tr -d '\0' или sed -e '1s/^\x0*//'.

  4. Управление пространством данных: Важно понимать, что удаление файла, в который продолжается запись, не освободит дисковое пространство, пока файл не будет закрыт всеми его процессами. В основе этой логики лежит работа файловых систем, например, временные файлы в Python функционируют именно так, чтобы защищать от утечек даже при аномальном завершении.

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

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

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