como alterar o formato da data em um arquivo de log usando bash, evitando o loop while

Sep 02 2020

Esta não é uma questão nova aqui e aqui , mas os detalhes a diferenciam.

Meu arquivo de registro de entrada se parece com:

TEMP MON -=- Sat Aug 15 02:20:24 EEST 2020 -=- 48.6
TEMP MON -=- Sat Aug 15 02:20:50 EEST 2020 -=- 49.1
TEMP MON -=- Sat Aug 15 02:21:13 EEST 2020 -=- 49.1
TEMP MON -=- Sat Aug 15 02:21:44 EEST 2020 -=- 49.1
TEMP MON -=- Sat Aug 15 02:21:45 EEST 2020 -=- 48.6
TEMP MON -=- Sat Aug 15 02:21:52 EEST 2020 -=- 49.1
TEMP MON -=- Sat Aug 15 02:21:53 EEST 2020 -=- 48.6
TEMP MON -=- Sat Aug 15 02:21:54 EEST 2020 -=- 49.6
TEMP MON -=- Sat Aug 15 02:21:56 EEST 2020 -=- 49.1
TEMP MON -=- Sat Aug 15 02:21:57 EEST 2020 -=- 49.1

e a saída deve ser semelhante a:

TEMP MON -=- 2020-08-15_02:20:24 EEST -=- 48.6
...

Portanto, é simples mudar o formato de uma data no bash usando

date -d ${date_in_current_format} "+DATE_IN_NEW_FORMAT"

Também é possível (embora ineficiente) iterar sobre o arquivo de log usando um loop while e alterar as datas linha por linha (consulte o primeiro link novamente).

No entanto, estou procurando uma solução bash que use sedou perl( awkou qualquer outra coisa) para realizar a mesma tarefa.

A dica do que tentei, mas ainda não funcionou, são as seguintes funções de pesquisa e substituição:

perl -pe "s/(.*) -=- (.*) -=- (.*)/\1 -=- $( date \2 "+%Z %Y-%m-%d_%H:%M:%S" ) -=- \3/" <file>

e com sedalgo semelhante:

sed "s:\(.*\) -=- \(.*\) -=- \(.*\):\1 -=- $( date -d \2 "+%Z %Y-%m-%d_%H:%M:%S" ) -=- \3:" <file>

Em ambos os casos, o problema é que não consigo fazer com que a substituição de pesquisa e substituição "\ 2" seja expandida na dateexecução do comando bash .

Respostas

4 thanasisp Sep 02 2020 at 15:09

Com awkusando apenas funções de cadeia, você pode evitar chamar as GNU awkfunções de data e hora ou o comando externo date, como nós queremos modificar apenas o mês e re-ordenar os dados.

> cat tst.awk
BEGIN { OFS=FS="-=-" }
{
    split($2, arr, " ") m=(index("JanFebMarAprMayJunJulAugSepOctNovDec", arr[2])+2)/3 $2=sprintf(" %04d-%02d-%02d_%s %s ", arr[6], m, arr[3], arr[4], arr[5])
    print
}

Uso:

> awk -f tst.awk file
TEMP MON -=- 2020-08-15_02:20:24 EEST -=- 48.6
TEMP MON -=- 2020-08-15_02:20:50 EEST -=- 49.1
TEMP MON -=- 2020-08-15_02:21:13 EEST -=- 49.1
TEMP MON -=- 2020-08-15_02:21:44 EEST -=- 49.1
TEMP MON -=- 2020-08-15_02:21:45 EEST -=- 48.6
TEMP MON -=- 2020-08-15_02:21:52 EEST -=- 49.1
TEMP MON -=- 2020-08-15_02:21:53 EEST -=- 48.6
TEMP MON -=- 2020-08-15_02:21:54 EEST -=- 49.6
TEMP MON -=- 2020-08-15_02:21:56 EEST -=- 49.1
TEMP MON -=- 2020-08-15_02:21:57 EEST -=- 49.1
3 anubhava Sep 02 2020 at 14:54

Você pode usar esta awksolução:

awk 'BEGIN {
   FS=OFS=" -=- "
}
{
   cmd = sprintf("TZ=EET date -d \"%s\" +\"%Y-%m-%%d_%T %Z\"", $2); if ((cmd | getline output) > 0) $2 = output
   close(cmd)
} 1' file
TEMP MON -=- 2020-08-15_02:20:24 EEST -=- 48.6
TEMP MON -=- 2020-08-15_02:20:50 EEST -=- 49.1
TEMP MON -=- 2020-08-15_02:21:13 EEST -=- 49.1
TEMP MON -=- 2020-08-15_02:21:44 EEST -=- 49.1
TEMP MON -=- 2020-08-15_02:21:45 EEST -=- 48.6
TEMP MON -=- 2020-08-15_02:21:52 EEST -=- 49.1
TEMP MON -=- 2020-08-15_02:21:53 EEST -=- 48.6
TEMP MON -=- 2020-08-15_02:21:54 EEST -=- 49.6
TEMP MON -=- 2020-08-15_02:21:56 EEST -=- 49.1
TEMP MON -=- 2020-08-15_02:21:57 EEST -=- 49.1
2 HåkonHægland Sep 02 2020 at 17:58

Usando o módulo principal Time::Pieceem Perl:

perl -MTime::Piece -pe 's/-=-\s+\K(.*)(?=\s+-=-)/convert($1)/e; sub convert { $s = $_[0]; $s =~ s/\s+EEST\s+/ /;
    $t = Time::Piece->strptime($s, "%a %b %d %T %Y");
    $res = $t->strftime("%Y-%m-%d_%H:%M:%S");
    "$res EEST"
 }' file