Como posso operar em \ 1 no sed (ou $ 1 no awk)?

2

Eu tenho arquivo com várias linhas. Cada um com um timestamp seguido por texto:

1534888050 some text
1534888051 some more text
1534888052 text

... e deseja converter o registro de data e hora em um formato legível, mantendo o texto. O resultado será:

Tue Aug 21 21:47:30 UTC 2018 some text
Tue Aug 21 21:47:31 UTC 2018 some more text
Tue Aug 21 21:47:32 UTC 2018 text

date -d@"${somedate}" converte o timestamp propriamente dito.

Agora eu quero o mesmo com um comando filter / pipe através de sed ou awk (ou qualquer outro comando comum).

Isso não funciona, mas explica o que estou pensando:

echo '1534888050 some text' | sed "s#^\([0-9]*\) .*#'date -d@'#g"

(O é o timestamp correspondente)

Qualquer maneira de conseguir isso de uma maneira elegante?

    
por kevcrumb 22.08.2018 / 00:25

6 respostas

3

O pacote dateutils é ótimo para essas coisas, especificamente a ferramenta dconv . (Quando instalado nos sistemas Ubuntu, o comando está disponível como dateutils.dconv .)

Aqui está uma ilustração:

$ cat testfile.txt 
1534888050 some text
1534888051 some more text
1534888052 text
$ dateutils.dconv -i '%s' -f '%a %b %d %T %Y' -S <testfile.txt 
Tue Aug 21 21:47:30 2018 some text
Tue Aug 21 21:47:31 2018 some more text
Tue Aug 21 21:47:32 2018 text
$ dateutils.dconv -i '%s' -f '%F %T' -S <testfile.txt 
2018-08-21 21:47:30 some text
2018-08-21 21:47:31 some more text
2018-08-21 21:47:32 text
$ 

O -S flag é a chave; ele diz à ferramenta para operar em "modo sed" e inclui o texto que não é data na saída.

O sinalizador -i especifica o formato da data de entrada e o sinal -f especifica o formato da data de saída.

    
por 22.08.2018 / 02:34
2

Se você tiver o GNU awk (também conhecido como gawk ), pode usar a função strftime integrada para converter um horário de época em um formato de sua escolha:

$ TZ=UTC gawk '{$1 = strftime("%a %b %d %H:%M:%S %Z %Y", $1)} 1' file
Tue Aug 21 21:47:30 UTC 2018 some text
Tue Aug 21 21:47:31 UTC 2018 some more text
Tue Aug 21 21:47:32 UTC 2018 text

Você também pode usar perl com o módulo POSIX :

$ TZ=UTC perl -MPOSIX -alne '
    print join " ", strftime("%a %b %d %H:%M:%S %Z %Y", localtime shift @F), @F
  ' file
Tue Aug 21 21:47:30 UTC 2018 some text
Tue Aug 21 21:47:31 UTC 2018 some more text
Tue Aug 21 21:47:32 UTC 2018 text

Embora a implementação GNU de sed tenha um modificador e para permitir a execução de comandos externos, ela opera em todo o espaço do padrão - tornando-o bastante complicado de usar na prática.

    
por 22.08.2018 / 00:45
0

Usando o Awk:

{
  "date -d @" $1 | getline $1
}
1

Resultado:

Tue, Aug 21, 2018  4:47:30 PM some text
Tue, Aug 21, 2018  4:47:31 PM some more text
Tue, Aug 21, 2018  4:47:32 PM text
    
por 22.08.2018 / 00:39
0

Se sed for uma obrigação, use o GNU sed com:

$ sed -e 'h;s/^\([^ ]*\) \(.*\)/date -ud @/;e' -e 'x;s///;x;G;s/\n/ /' infile.txt
Tue Aug 21 21:47:30 UTC 2018 some text
Tue Aug 21 21:47:31 UTC 2018 some more text
Tue Aug 21 21:47:32 UTC 2018 text

Caso contrário, o shell poderia fazer:

$ TZ=UTC0 bash -c 'while read a b; do printf '\''%(%c)T %s\n'\'' "$a" "$b"; done <infile'
Tue 21 Aug 2018 09:47:30 PM UTC some text
Tue 21 Aug 2018 09:47:31 PM UTC some more text
Tue 21 Aug 2018 09:47:32 PM UTC text

Ou, melhor ainda, use o awk:

$ awk '{c="date -ud @" $1;c|getline $1;close(c)}1' infile.txt
Tue Aug 21 21:47:30 UTC 2018 some text
Tue Aug 21 21:47:31 UTC 2018 some more text
Tue Aug 21 21:47:32 UTC 2018 text
    
por 22.08.2018 / 04:34
0

O que você precisa perceber é que GNU sed substitui todo o espaço padrão com o resultado da avaliação comnand no RHS de s///e . Então, para acomodar esse comportamento, usamos a combinação de comandos echo + date , conforme mostrado:

 sed -e  's/^\([1-9][0-9]*\)\(.*\)/echo "'date -d@\"\"'"/e'  input-file.txt

E com Perl , é simples, o que é correspondido no LHS só é substituído no RHS, como se esperaria intuitivamente também:

  perl -pe 's/(\d+)/qx(date -d@"$1") =~ s|\n||r/e'  input-file.txt 
    
por 22.08.2018 / 18:50
0

No awk você pode usar

awk '{ cmd = "date -d@" $1; cmd | getline date; close (cmd); $1 = date; print $0; }'

Para restringir a valores numéricos, verifique o padrão de apenas dígitos:

awk '$1 ~ /^[0-9]*$/ { cmd = "date -d@" $1; cmd | getline $1; close (cmd); print $0; }'
    
por 22.08.2018 / 00:39