Respostas:
$ cat input.log | sed -e "s/^/$(date -R) /" >> output.log
Como funciona:
cat
lê o arquivo chamado input.log
e apenas o imprime no fluxo de saída padrão.
Normalmente, a saída padrão está conectada a um terminal, mas este pequeno script contém, |
portanto, o shell redireciona a saída cat
padrão para a entrada padrão de sed
.
sed
lê dados (como os cat
produz), processa-os (de acordo com o script fornecido com a -e
opção) e depois os imprime em sua saída padrão. O script "s/^/$(date -R) /"
significa substituir todo início de linha por um texto gerado por date -R
comando (a construção geral do comando replace é:) s/pattern/replace/
.
Então, de acordo com >>
bash
redireciona a saída de sed
para um arquivo chamado output.log
( >
significa substituir o conteúdo do arquivo e >>
significa anexar ao final).
O problema é $(date -R)
avaliado uma vez quando você executa o script para inserir o registro de data e hora atual no início de cada linha. O registro de data e hora atual pode estar longe de um momento em que uma mensagem foi gerada. Para evitá-lo, é necessário processar as mensagens conforme elas são gravadas no arquivo, não com uma tarefa cron.
O redirecionamento de fluxo padrão descrito acima chamado pipe . Você pode redirecioná-lo não apenas com |
comandos no script, mas também através de um arquivo FIFO (também conhecido como pipe ). Um programa gravará no arquivo e outro lerá os dados e os receberá quando o primeiro enviar.
Escolha um exemplo:
$ mkfifo foo.log.fifo
$ while true; do cat foo.log.fifo | sed -e "s/^/$(date -R) /" >> foo.log; done;
# have to open a second terminal at this point
$ echo "foo" > foo.log.fifo
$ echo "bar" > foo.log.fifo
$ echo "baz" > foo.log.fifo
$ cat foo.log
Tue, 20 Nov 2012 15:32:56 +0400 foo
Tue, 20 Nov 2012 15:33:27 +0400 bar
Tue, 20 Nov 2012 15:33:30 +0400 baz
Como funciona:
mkfifo
cria um pipe nomeado
while true; do sed ... ; done
executa um loop infinito e a cada iteração é executado sed
com o redirecionamento foo.log.fifo
para sua entrada padrão; sed
bloqueia a espera dos dados de entrada , processa uma mensagem recebida e a imprime na saída padrão redirecionada para foo.log
.
Nesse ponto, você precisa abrir uma nova janela do terminal porque o loop ocupa o terminal atual.
echo ... > foo.log.fifo
imprime uma mensagem em sua saída padrão redirecionada para o arquivo fifo e a sed
recebe, processa e grava em um arquivo regular.
A nota importante é o fifo, assim como qualquer outro tubo não faz sentido se um de seus lados não estiver conectado a nenhum processo. Se você tentar gravar em um canal, o processo atual será bloqueado até que alguém leia os dados do outro lado do canal. Se você quiser ler de um canal, o processo será bloqueado até que alguém escreva dados no canal. O sed
loop no exemplo acima não faz nada (dorme) até você fazer echo
.
Para sua situação específica, basta configurar seu aplicativo para gravar mensagens de log no arquivo fifo. Se você não pode configurá-lo - simplesmente exclua o arquivo de log original e crie um arquivo fifo. Mas observe novamente que, se o sed
loop morrer por algum motivo - seu programa será bloqueado ao tentar acessar write
o arquivo até que alguém read
o faça do fifo.
O benefício é o carimbo de data / hora atual avaliado e anexado a uma mensagem enquanto o programa a grava no arquivo.
tailf
Para tornar a gravação no log e o processamento mais independentes, você pode usar dois arquivos regulares com tailf
. Um aplicativo grava a mensagem em um arquivo bruto e outro processo lê novas linhas (siga as gravações de forma assíncrona) e processa os dados gravando no segundo arquivo.
Vamos dar um exemplo:
# will occupy current shell
$ tailf -n0 bar.raw.log | while read line; do echo "$(date -R) $line" >> bar.log; done;
$ echo "foo" >> bar.raw.log
$ echo "bar" >> bar.raw.log
$ echo "baz" >> bar.raw.log
$ cat bar.log
Wed, 21 Nov 2012 16:15:33 +0400 foo
Wed, 21 Nov 2012 16:15:36 +0400 bar
Wed, 21 Nov 2012 16:15:39 +0400 baz
Como funciona:
Execute o tailf
processo que seguirá as gravações bar.raw.log
e as imprimirá na saída padrão redirecionada para o while read ... echo
loop infinito . Esse loop executa duas ações: ler dados da entrada padrão em uma variável de buffer chamada line
e, em seguida, gravar o registro de data e hora gerado com os seguintes dados em buffer no bar.log
.
Escreva algumas mensagens para o bar.raw.log
. Você deve fazer isso em uma janela de terminal separada, porque a primeira ocupará a tailf
qual seguirá as gravações e fará seu trabalho. Bem simples.
A vantagem é que seu aplicativo não bloquearia se você matar tailf
. Os contras são carimbos de data e hora menos precisos e duplicação de arquivos de log.
tailf
, adicionado o caminho certo para usá-lo. Na verdade, o caminho com tailf
parece ser mais elegante, mas saí do quinto caminho com esperança de que seja útil para alguém.
Você pode usar o ts
script perl em moreutils
:
$ echo test | ts %F-%H:%M:%.S
2012-11-20-13:34:10.731562 test
Modificado a partir da resposta de Dmitry Vasilyanov.
No script bash, você pode redirecionar e agrupar a saída com registro de data e hora linha por linha em tempo real.
Quando usar:
tailf
o arquivo de log, como disse Dmitry Vasilyanov.Um exemplo chamado foo.sh
:
#!/bin/bash
exec &> >(while read line; do echo "$(date +'%h %d %H:%M:%S') $line" >> foo.log; done;)
echo "foo"
sleep 1
echo "bar" >&2
sleep 1
echo "foobar"
E o resultado:
$ bash foo.sh
$ cat foo.log
May 12 20:04:11 foo
May 12 20:04:12 bar
May 12 20:04:13 foobar
Como funciona
exec &>
Redirecionar stdout e stderr para o mesmo local>( ... )
canaliza saídas para um comando interno assíncronoPor exemplo:
registro de data e hora do pipe e log para arquivo
#!/bin/bash
exec &> >(while read line; do echo "$(date +'%h %d %H:%M:%S') $line" >> foo.log; done;)
echo "some script commands"
/path-to/some-thrid-party-programs
Ou imprima o carimbo de data e hora e faça logon no stdout
#!/bin/bash
exec &> >(while read line; do echo "$(date +'%h %d %H:%M:%S') $line"; done;)
echo "some script commands"
/path-to/some-thrid-party-programs
salve-os na /etc/crontab
configuração
* * * * * root /path-to-script/foo.sh >> /path-to-log-file/foo.log
Usei ts
dessa maneira para obter uma entrada com registro de data e hora em um log de erro de um script que eu uso para obter o Cacti preenchido com estatísticas de um host remoto.
Para testar o Cacti, eu rand
adiciono alguns valores aleatórios que eu uso nos gráficos de temperatura para monitorar a temperatura do meu sistema.
Pushmonstats.sh é um script que coleta estatísticas de temperatura do sistema do meu PC e envia para um Raspberry Pi no qual o Cacti é executado. Algum tempo atrás, a rede estava presa. Eu só tenho tempos limite de SSH no meu log de erros. Infelizmente, não há entradas de tempo nesse log. Não sabia como adicionar um carimbo de data / hora a uma entrada de log. Então, depois de algumas pesquisas na Internet, me deparei com este post e foi isso que fiz usando ts
.
Para testá-lo, usei uma opção desconhecida para rand
. O que deu um erro ao stderr. Para capturá-lo, eu o redireciono para um arquivo temporário. Então eu uso o gato para mostrar o conteúdo do arquivo e canalizá-lo ts
, adicionar um formato de hora que encontrei nesta postagem e finalmente registrá-lo no arquivo de erro. Em seguida, limpo o conteúdo do arquivo temporário, caso contrário, recebo entradas duplas pelo mesmo erro.
Crontab:
* * * * * /home/monusr/bin/pushmonstats.sh 1>> /home/monusr/pushmonstats.log 2> /home/monusr/.err;/bin/cat /home/monusr/.err|/usr/bin/ts %F-%H:%M:%.S 1>> /home/monusr/pushmonstats.err;> /home/monusr/.err
Isso fornece o seguinte no meu log de erros:
2014-03-22-19:17:53.823720 rand: unknown option -- '-l'
Talvez essa não seja uma maneira muito elegante de fazê-lo, mas funciona. Gostaria de saber se existe uma abordagem mais elegante.