TL; DR
Abra seu arquivo de log no modo de acréscimo :
cmd >> log
Em seguida, você pode truncá-lo com segurança com:
: > log
Detalhes
Com um shell semelhante ao Bourne, existem três maneiras principais de abrir um arquivo para gravação. No modo somente gravação ( >), leia + escreva ( <>) ou acrescente (e somente gravação >>).
Nos dois primeiros, o kernel se lembra da posição atual em que você (por você, quero dizer, a descrição do arquivo aberto , compartilhada por todos os descritores de arquivo que o duplicaram ou herdaram ao bifurcar aquele em que você abriu o arquivo) está no diretório Arquivo.
Quando você faz:
cmd > log
logestá aberto no modo somente gravação pelo shell para o stdout de cmd.
cmd(seu processo inicial gerado pelo shell e todos os filhos possíveis) ao escrever em seu stdout, escreva na posição atual do cursor mantida pela descrição do arquivo aberto que eles compartilham nesse arquivo.
Por exemplo, se gravar cmdinicialmente zzz, a posição será no deslocamento de bytes 4 no arquivo e, na próxima vez em que cmdseus filhos gravarem no arquivo, é onde os dados serão gravados, independentemente de o arquivo ter aumentado ou diminuído no intervalo .
Se o arquivo encolheu, por exemplo, se ele foi truncado com um
: > log
e cmdgravações xx, elas xxserão gravadas em offset 4e os 3 primeiros caracteres serão substituídos por caracteres 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
Isso significa que você não pode truncar um arquivo que foi aberto no modo somente gravação (e é o mesmo para leitura + gravação ) como se o fizesse, processos que tinham descritores de arquivo abertos no arquivo, deixarão caracteres NUL no início do arquivo (aqueles, exceto no OS / X, normalmente não ocupam espaço no disco, eles se tornam arquivos esparsos).
Em vez disso (e você notará que a maioria dos aplicativos faz isso quando escreve nos arquivos de log), você deve abrir o arquivo no modo de acréscimo :
cmd >> log
ou
: > log && cmd >> log
se você deseja iniciar em um arquivo vazio.
No modo de acréscimo, todas as gravações são feitas no final do arquivo, independentemente de onde a última gravação foi:
$ 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
Isso também é mais seguro, como se dois processos tivessem aberto (dessa maneira) o arquivo por engano (como por exemplo, se você iniciou duas instâncias do mesmo daemon), a saída deles não será substituída.
Nas versões recentes do Linux, você pode verificar a posição atual e se um descritor de arquivo foi aberto no modo de acréscimo , observando /proc/<pid>/fdinfo/<fd>:
$ cat /proc/self/fdinfo/4
pos: 2
flags: 0102001
Ou com:
$ 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
Esses sinalizadores correspondem aos sinalizadores O ..._ passados para a openchamada do sistema.
$ gcc -E - <<< $'#include <fcntl.h>\nO_APPEND O_WRONLY' | tail -n1
02000 01
( O_APPENDé 0x400 ou octal 02000)
Portanto, o shell >>abre o arquivo com O_WRONLY|O_APPEND(e 0100000 aqui é O_LARGEFILE, o que não é relevante para esta questão) enquanto >é O_WRONLYapenas (e <>é O_RDWRapenas).
Se você fizer um:
sudo lsof -nP +f g | grep ,AP
para procurar arquivos abertos O_APPEND, você encontrará a maioria dos arquivos de log atualmente abertos para gravação em seu sistema.