TL; DR
Откройте файл журнала в режиме добавления :
cmd >> log
Затем вы можете безопасно обрезать его:
: > log
Детали
В борновоподобной оболочке существует 3 основных способа открытия файла для записи. В режиме только для записи ( >), чтение + запись ( <>) или добавление (и только запись >>).
В первых двух ядро запоминает текущую позицию, в которой вы (под вами, я имею в виду, описание открытого файла , разделяемое всеми дескрипторами файлов, которые продублировали или унаследовали его путем разветвления от того, на котором вы открыли файл), находитесь в файл.
Когда вы делаете:
cmd > log
logоткрыт в режиме « только для записи» оболочкой для вывода cmd.
cmd(его начальный процесс порожден оболочкой и всеми возможными дочерними элементами) при записи в их стандартный вывод записывать в текущей позиции курсора, содержащейся в открытом описании файла, которое они разделяют в этом файле.
Например, при cmdпервоначальной записи zzzпозиция будет иметь байтовое смещение 4 в файле, а в следующий раз cmdили когда его дочерние элементы будут записывать в файл, именно туда будут записываться данные независимо от того, вырос ли файл или сократился в интервале ,
Если файл сжался, например, если он был усечен с
: > log
и cmdпишет xx, те xxбудут записаны со смещением 4, и первые 3 символа будут заменены на NUL символов.
$ exec 3> log # open file on fd 3.
$ printf zzz >&3
$ od -c log
0000000 z z z
0000003
$ printf aaaa >> log # other open file description -> different cursor
$ od -c log
0000000 z z z a a a a
0000007
$ printf bb >&3 # still write at the original position
$ od -c log
0000000 z z z b b a a
0000007
$ : > log
$ wc log
0 0 0 log
$ printf x >&3
$ od -c log
0000000 \0 \0 \0 \0 \0 x
0000006
Это означает, что вы не можете усечь файл, который был открыт в режиме только для записи (и то же самое для чтения + записи ), как если бы вы это делали, процессы, у которых в файле были открыты файловые дескрипторы, оставят NUL-символы в начале file (те, за исключением OS / X, обычно не занимают место на диске, хотя они становятся разреженными файлами).
Вместо этого (и вы заметите, что большинство приложений делают это, когда они пишут в файлы журналов), вы должны открыть файл в режиме добавления :
cmd >> log
или
: > log && cmd >> log
если вы хотите начать с пустого файла.
В режиме добавления все записи производятся в конце файла, независимо от того, где была последняя запись:
$ exec 4>> log
$ printf aa >&4
$ printf x >> log
$ printf bb >&4
$ od -c log
0000000 a a x b b
0000005
$ : > log
$ printf cc >&4
$ od -c log
0000000 c c
0000002
Также безопаснее, если два процесса по ошибке открыли (таким образом) файл (например, если вы запустили два экземпляра одного и того же демона), их выходные данные не будут перезаписывать друг друга.
В последних версиях Linux вы можете проверить текущую позицию и был ли дескриптор файла открыт в режиме добавления , посмотрев /proc/<pid>/fdinfo/<fd>:
$ cat /proc/self/fdinfo/4
pos: 2
flags: 0102001
Или с:
$ lsof +f G -p "$$" -ad 4
COMMAND PID USER FD TYPE FILE-FLAG DEVICE SIZE/OFF NODE NAME
zsh 4870 root 4w REG 0x8401;0x0 252,18 2 59431479 /home/chazelas/log
~# lsof +f g -p "$$" -ad 4
COMMAND PID USER FD TYPE FILE-FLAG DEVICE SIZE/OFF NODE NAME
zsh 4870 root 4w REG W,AP,LG 252,18 2 59431479 /home/chazelas/log
Эти флаги соответствуют флагам O ..._, передаваемым openсистемному вызову.
$ gcc -E - <<< $'#include <fcntl.h>\nO_APPEND O_WRONLY' | tail -n1
02000 01
( O_APPEND0x400 или восьмеричное 02000)
Так оболочечному >>открывает файл с O_WRONLY|O_APPEND(и 0100000 здесь O_LARGEFILE , которая не имеет отношения к этому вопросу) в то время как >это O_WRONLYтолько (и <>это O_RDWRтолько).
Если вы делаете:
sudo lsof -nP +f g | grep ,AP
для поиска файлов, открытых с помощью O_APPEND, вы найдете большинство файлов журналов, открытых на данный момент для записи в вашей системе.