Довольно популярный и актуальный вопрос, который возникает, например, при чистке интенсивно растущих журналов постоянно запущенных процессов.
Ответ в двух словах
Обнулять можно, но для этого пишущий процесс должен открывать этот файл с использованием флага O_APPEND. Ниже приводится соответствующий отрывок из open(2):
…
O_APPEND
The file is opened in append mode. Before eachwrite(2), the file offset is positioned at the end of the file, as if withlseek(2).O_APPENDmay lead to corrupted files on NFS file systems if more than one process appends data to a file at once. This is because NFS does not support appending to a file, so the client kernel has to simulate it, which can't be done without a race condition.
…
Несколько процессов, пишущих в один файл на NFS — это отдельный клинический случай. Пусть у нас есть один-единственный процесс, пишущий в файл.
Как же быть, если наш процесс не использует флаг O_APPEND?
Есть два варианта решения в зависимости от того, как создается этот файл:
- Путем перенаправления стандартных потоков вывода (stdout/stderr):
$ /path/binary >/path/log 2>&1Здесь при открытии файла флаг
O_APPENDбудет использоваться в том случае, если вместо оператора перенаправления>использовать оператор перенаправления с добавлением>>:$ /path/binary >>/path/log 2>&1 - Процесс пишет напрямую в предопределенный файл
/path/log_predefined. В этом случае перед запуском процесса создается связка из именованного канала (named pipe) и, например,cat(1), приводящая этот случай к предыдущему:$ mkfifo /path/log_predefined && cat </path/log_predefined >>/path/log &Кстати, при завершении нашего процесса «фоновый» cat будет также завершаться. Как вариант решения этой проблемы можно запускать cat в бесконечном цикле:
$ bash -c "while :; do cat <log_predefined >>log; done" &
Теперь можно безболезненно обнулить файл журнала в любой момент, например, так:
$ : > /path/log
Однако следует заметить, что флаг O_APPEND не поддерживается некоторыми старыми оболочками, например, Bourne shell (/bin/sh), все еще использующимся в большинстве скриптов.
Проверим это утверждение, например, в Solaris. При этом предупрежу заранее, что пользователям Linux проблема со старой /bin/sh не грозит, т.к.:
$ uname -o && ls -l /bin/sh
GNU/Linux
lrwxrwxrwx 1 root root 4 2008-11-10 17:16 /bin/sh -> bash
Открытие файлов оператором '>>' в разных оболочках
Приведённый ниже скрипт выполняет команду echo c перенаправлением вывода в новый или существующий файл оператором '>>' в разных оболочках, отслеживая при этом интересующие нас системные вызовы creat(2), open(2), lseek(2) и их варианты:
1 #!/bin/sh
2
3 trace() {
4 eval "truss -tcreat,open,lseek $* 2>&1 | sed -ne '/my_test/,$p;'"
5 }
6
7 for shell in sh csh tcsh ksh bash zsh; do
8 printf "nRedirecting with '>>' to a non-existing file in '$shell'n"
9 [ -f my_test ] && rm my_test
10 trace "$shell -c 'echo test >> my_test'"
11
12 printf "nRedirecting with '>>' to an existing file in '$shell'n"
13 [ -f my_test ] || : > my_test
14 trace "$shell -c 'echo test >> my_test'"
15 done
В следующей таблице приводятся результаты нашего исследования — флаги системного вызова open(2), использовавшиеся оболочками для открытия нового или существующего файла оператором '>>'. При этом учитывается, что системный вызов creat(2) является синонимом open(), вызванным с флагами O_WRONLY | O_CREAT | O_TRUNC.
| Оболочка | Файл | |
|---|---|---|
| новый | существующий | |
| sh csh |
O_WRONLY | O_CREAT | O_TRUNC | O_WRONLY |
| tcsh | O_WRONLY | O_CREAT | O_TRUNC | O_WRONLY | O_APPEND |
| bash ksh |
O_WRONLY | O_APPEND | O_CREAT | |
| zsh | O_WRONLY | O_APPEND | O_CREAT | O_NOCTTY | |
Флаг O_NOCTTY в zsh используется на тот случай, если в качестве имени файла будет указано терминальное устройство, см. open(2):
…
O_NOCTTY
If set and path identifies a terminal device,open()does not cause the terminal device to become the controlling terminal for the process.
…
Таким образом, оболочки sh и csh, а также tcsh при открытии нового файла, не используют флаг O_APPEND, а описанный выше способ обнуления используемых файлов в этих оболочках использовать не получится. В более современных оболочках (ksh, bash, zsh) этой проблемы нет.
В следующей статье я постараюсь разобраться подробнее в нюансах работы со стандартными потоками и каналами в UNIX применительно к задаче ротации журналов.
Автор: nfubh
