Existe um utilitário Unix para acrescentar carimbos de data e hora ao stdin?


168

Acabei escrevendo um pequeno script rápido para isso em Python, mas fiquei imaginando se havia um utilitário no qual você pudesse alimentar o texto que precederia cada linha com algum texto - no meu caso específico, um carimbo de data / hora. Idealmente, o uso seria algo como:

cat somefile.txt | prepend-timestamp

(Antes de responder sed, tentei o seguinte:

cat somefile.txt | sed "s/^/`date`/"

Mas isso avalia apenas o comando date uma vez quando o sed é executado; portanto, o mesmo timestamp incorretamente é anexado incorretamente a cada linha.)


Isso é cat somefile.txtum pouco "enganoso"? Eu esperava que isso acontecesse "de uma só vez" e tivesse um único registro de data e hora. Este não seria um programa de teste melhor (echo a; sleep 1; echo b; sleep 3; echo c; sleep 2):?
Ciro Santilli escreveu:

Respostas:


185

Pode tentar usar awk:

<command> | awk '{ print strftime("%Y-%m-%d %H:%M:%S"), $0; fflush(); }'

Você pode precisar se certificar de que <command>produz saída em buffer de linha, ou seja, libera seu fluxo de saída após cada linha; o registro de data e hora awkadiciona será o horário em que o final da linha apareceu em seu canal de entrada.

Se o awk mostrar erros, tente gawk.


31
No meu sistema, o 'awk' estava em buffer. (Isso pode ser problemático para arquivos de log). Corrigi isso usando: awk '{print strftime ("% Y-% m-% dT% H:% M:% S"), $ 0; fflush (); } '
user47741 11/03/10

19
strftime () parece ser uma extensão GNU awk; portanto, se você estiver no Mac OS, por exemplo, use gawk em vez de awk.
Joe Shaw

Não deixa de me surpreender o quão poderoso o bash é. Não achei que houvesse uma solução tão fácil para essa tarefa complicada.
recluze

2
Para usuários do OS X, é necessário instalar gawke usar isso em vez de awk. Se você tem MacPorts:sudo port install gawk
Matt Williamson

5
@teh_senaus Até onde eu sei, o awk's strftime()não tem precisão de milissegundos. No entanto, você pode usar o datecomando Basicamente, você apenas apara nanossegundos para três caracteres. Ele será parecido com este: COMMAND | while read -r line; do echo "$(date '+%Y-%m-%d %T.%3N') $line"; done. Observe que você pode abreviar% H:% M:% S com% T. Se você ainda deseja usá-lo awkpor algum motivo, pode fazer o seguinte:COMMAND | awk '{ "date +%Y-%m-%d\\ %T.%3N" | getline timestamp; print timestamp, $0; fflush(); }'
Seis

190

tsde moreutils irá anteceder um timestamp para cada linha de entrada que dá-lo. Você pode formatá-lo usando strftime também.

$ echo 'foo bar baz' | ts
Mar 21 18:07:28 foo bar baz
$ echo 'blah blah blah' | ts '%F %T'
2012-03-21 18:07:30 blah blah blah
$ 

Para instalá-lo:

sudo apt-get install moreutils

2
capturar e carimbar stderr ao mesmo tempo: bad command 2>&1 | tsresulta emJan 29 19:58:31 -bash: bad: command not found
rymo

Eu quero usar o formato da hora 2014 Mar 21 18:07:28. Como faço para obtê-lo?
Becko

@ Becko - de acordo com a página do manual , forneça uma strftimestring de formato como argumento :, [.. command ..] | ts '%Y %b %d %T'por exemplo.
precisa

2
Para o OS X brew install moreutils,. Para ls -l |TZ=UTC ts '%Y-%m-%dT%H:%M:%SZ'
gerar o

1
@Dorian isso ocorre porque o ruby ​​está armazenando a saída em buffer; se você liberar o buffer antes de dormir funciona bem o suficiente:ruby -e "puts 1; STDOUT.flush; sleep 1; puts 2; STDOUT.flush; sleep 2; puts 3" | ts '%F %T'
trema

45

anote , disponível através desse link ou como annotate-outputno devscriptspacote Debian .

$ echo -e "a\nb\nc" > lines
$ annotate-output cat lines
17:00:47 I: Started cat lines
17:00:47 O: a
17:00:47 O: b
17:00:47 O: c
17:00:47 I: Finished with exitcode 0

1
Essa é uma ótima solução, mas não funcionará com saída canalizada ou saída com subconjunto. Apenas reformatará a saída para um programa ou script que você fornecer como argumento.
SLM

1
A anotação de saída funciona bem e é fácil de usar, mas é REALMENTE lenta.
Paul Brannan

31

Destilando as respostas dadas para a mais simples possível:

unbuffer $COMMAND | ts

No Ubuntu, eles vêm dos pacotes expect-dev e moreutils.

sudo apt-get install expect-dev moreutils

1
É expect, não expect-dev, mas o último puxa na antiga assim isso funciona. (Ubuntu 14.10, eu me pergunto se o binário foi transferido para um pacote diferente em algum ponto.)
Marius Gedminas

Para o Ubuntu 14.04 LTS, ele ainda está emexpect-dev
Willem

2
Se você gostaria de obter tempo decorrido com precisão milissegundo, o uso unbuffer $COMMAND | ts -s %.s( -sse por tempo decorrido e %.spara o tempo em segundos com parte fracionária)
Greg Witczak

24

Que tal agora?

cat somefile.txt | perl -pne 'print scalar(localtime()), " ";'

A julgar pelo seu desejo de obter registros de data e hora ao vivo, talvez você queira atualizar ao vivo em um arquivo de log ou algo assim? Talvez

tail -f /path/to/log | perl -pne 'print scalar(localtime()), " ";' > /path/to/log-with-timestamps

4
Este exemplo perl foi particularmente útil para mim, pois estava trabalhando como usuário em uma implantação do ubuntu, que aparentemente usa 'mawk' em vez de 'gawk' - e não possui a função strftime. Obrigado!
Opello 19/08/10

4
+1 para isso ser útil em uma instalação padrão do Ubuntu. Eu costumava tail -f /path/to/log | perl -pne 'use POSIX qw(strftime); print strftime "%Y-%m-%dT%H:%M:%SZ ", gmtime();'obter uma data no estilo ISO8601 / RFC3339, em vez da wacko que a versão mais simples gera.
Natevw #

1
Se você quiser ver a saída do registro com registro de data e hora, ela pode ser útil adicionar BEGIN { $|++; }antes da instrução de impressão para que o Perl libere sua saída com cada linha.

2
Você pode diminuir ainda mais o script perl ls | perl -pne 'print localtime." "'. A conversão escalar ocorre devido à concatenação de cadeias.
rahul

Por que você usa ambos -pe -nopções? Parece que -né redundante se você especificou -p.
Davor Cubranic em


18

A resposta de Kieron é a melhor até agora. Se você tiver problemas porque o primeiro programa está armazenando o buffer, você pode usar o programa unbuffer:

unbuffer <command> | awk '{ print strftime("%Y-%m-%d %H:%M:%S"), $0; }'

É instalado por padrão na maioria dos sistemas Linux. Se você precisar construí-lo, isso faz parte do pacote expect

http://expect.nist.gov


Note que eu descobri que 'unbuffer' às vezes esconde o resultado de retorno do programa chamado - por isso não é útil em Jenkins ou outras coisas que dependem do status de retorno
oskarpearson

10

Use o comando read (1) para ler uma linha de cada vez da entrada padrão e, em seguida, imprima a linha anexada com a data no formato de sua escolha usando a data (1).

$ cat timestamp
#!/bin/sh
while read line
do
  echo `date` $line
done
$ cat somefile.txt | ./timestamp

2
Um pouco pesado, pois bifurca um novo processo por linha, mas funciona!
Joe Shaw

3

Eu não sou um cara Unix, mas acho que você pode usar

gawk '{print strftime("%d/%m/%y",systime()) $0 }' < somefile.txt

3
#! /bin/sh
unbuffer "$@" | perl -e '
use Time::HiRes (gettimeofday);
while(<>) {
        ($s,$ms) = gettimeofday();
        print $s . "." . $ms . " " . $_;
}'

2

Aqui está minha solução awk (de um sistema Windows / XP com o MKS Tools instalado no diretório C: \ bin). Ele foi projetado para adicionar a data e a hora atuais no formato mm / dd hh: mm ao início de cada linha, tendo buscado o registro de data e hora do sistema à medida que cada linha é lida. Obviamente, você pode usar o padrão BEGIN para buscar o carimbo de data / hora uma vez e adicioná-lo a cada registro (todos iguais). Fiz isso para marcar um arquivo de log que estava sendo gerado para stdout com o carimbo de data e hora no momento em que a mensagem de log foi gerada.

/"pattern"/ "C\:\\\\bin\\\\date '+%m/%d %R'" | getline timestamp;
print timestamp, $0;

onde "padrão" é uma string ou regex (sem as aspas) a serem correspondidas na linha de entrada e é opcional se você deseja corresponder a todas as linhas de entrada.

Isso deve funcionar em sistemas Linux / UNIX também, basta se livrar do C \: \\ bin \\ saindo da linha

             "date '+%m/%d %R'" | getline timestamp;

Obviamente, isso pressupõe que o comando "date" leva você ao comando padrão de exibição / configuração da data do Linux / UNIX sem informações específicas do caminho (ou seja, a variável PATH do ambiente está configurada corretamente).


No OS XI, era necessário um espaço antes do comando date. Infelizmente, não parece reavaliar o comando para cada linha. Como outros sugeriram, estou tentando adicionar registros de data e hora para tail -f.
MarkBennett

2

Misturando algumas respostas acima de natevw e Frank Ch. Eigler.

Possui milissegundos, executa melhor do que chamar um datecomando externo toda vez e o perl pode ser encontrado na maioria dos servidores.

tail -f log | perl -pne '
  use Time::HiRes (gettimeofday);
  use POSIX qw(strftime);
  ($s,$ms) = gettimeofday();
  print strftime "%Y-%m-%dT%H:%M:%S+$ms ", gmtime($s);
  '

Versão alternativa com flush e leia em um loop:

tail -f log | perl -pne '
  use Time::HiRes (gettimeofday); use POSIX qw(strftime);
  $|=1;
  while(<>) {
    ($s,$ms) = gettimeofday();
    print strftime "%Y-%m-%dT%H:%M:%S+$ms $_", gmtime($s);
  }'

1
Duas falhas acima: 1. $ ms não está alinhado e não é preenchido com zero. 2. Quaisquer códigos% no log serão desconfigurados. Assim, em vez da linha de impressão, eu usei: printf "%s+%06d %s", (strftime "%Y-%m-%dT%H:%M:%S", gmtime($s)), $ms, $_;
Simon Pickup

2
$ cat somefile.txt | sed "s/^/`date`/"

você pode fazer isso (com gnu / sed ):

$ some-command | sed "x;s/.*/date +%T/e;G;s/\n/ /g"

exemplo:

$ { echo 'line1'; sleep 2; echo 'line2'; } | sed "x;s/.*/date +%T/e;G;s/\n/ /g"
20:24:22 line1
20:24:24 line2

é claro, você pode usar outras opções da data do programa . apenas substitua date +%Tpelo que você precisa.


1

A resposta de caerwyn pode ser executada como uma sub-rotina, o que impediria os novos processos por linha:

timestamp(){
   while read line
      do
         echo `date` $line
      done
}

echo testing 123 |timestamp

3
como isso impede que dateseja gerado em todas as linhas?
precisa saber é o seguinte

@ Gymom Não impede que a data seja gerada em todas as linhas. Impede a concha de gerar todas as linhas. Eu acrescentaria que no bash você poderia fazer algo como: timestamp() { while read line; do echo "$(date) $line"; done; }; export -f timestampem um script que você forneça.
Smbear

A data da chamada como um subprocesso ainda significa gerá-la para todas as linhas, no entanto, você inicia o script em primeiro lugar.
Peter Hansen

3
Para evitar o comando de geração datepara todas as linhas, tente usar o bash printfembutido com o formato T% (dataspec) . Então pode parecer timestamp() { while IFS= read -r line; do printf "%(%F %T)T %s\n" -1 "$line"; done; }. Os componentes da Shell não aparecerão. Formato Datespec é como strftime(3)ie dateformatos. Isso requer bash, não sei desde que versão ele suporta esse printfespecificador.
Mindaugas Kubilius

1

aviso Legal : a solução que estou propondo não é um utilitário interno do Unix.

Eu enfrentei um problema semelhante há alguns dias. Não gostei da sintaxe e das limitações das soluções acima, então montei rapidamente um programa no Go para fazer o trabalho por mim.

Você pode verificar a ferramenta aqui: pré - horário

Existem executáveis ​​pré-criados para Linux, MacOS e Windows na seção Versões do projeto GitHub.

A ferramenta lida com linhas de saída incompletas e possui (do meu ponto de vista) uma sintaxe mais compacta.

<command> | preftime

Não é o ideal, mas eu gostaria de compartilhá-lo, caso isso ajude alguém.


0

fazê-lo com datee tre xargsno OSX:

alias predate="xargs -I{} sh -c 'date +\"%Y-%m-%d %H:%M:%S\" | tr \"\n\" \" \"; echo \"{}\"'"
<command> | predate

se você quiser milissegundos:

alias predate="xargs -I{} sh -c 'date +\"%Y-%m-%d %H:%M:%S.%3N\" | tr \"\n\" \" \"; echo \"{}\"'"

mas observe que no OSX, date não oferece a opção% N, então você precisará instalar o gdate ( brew install coreutils) e finalmente chegar a isso:

alias predate="xargs -I{} sh -c 'gdate +\"%Y-%m-%d %H:%M:%S.%3N\" | tr \"\n\" \" \"; echo \"{}\"'"

-1

Se o valor que você está anexando for o mesmo em todas as linhas, inicie o emacs com o arquivo e:

Ctrl + <space>

no início do arquivo (para marcar esse ponto), role para baixo até o início da última linha (Alt +> irá para o final do arquivo ... o que provavelmente envolverá também a tecla Shift e, em seguida, Ctrl + a para ir para o início dessa linha) e:

Ctrl + x r t

Qual é o comando para inserir no retângulo que você acabou de especificar (um retângulo de 0 de largura).

21/08/2008 18:45 <enter>

Ou o que você quiser anexar ... então você verá esse texto anexado a todas as linhas dentro do retângulo com 0 largura.

ATUALIZAÇÃO: Acabei de perceber que você não quer a mesma data, então isso não funcionará ... embora você possa fazer isso no emacs com uma macro personalizada um pouco mais complicada, mas ainda assim, esse tipo de edição de retângulo é muito bom saber sobre ...

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.