GAWK / AWK: Data da tubulação para GetLine * às vezes * não funciona
Pergunta
Estou tentando converter datas de um formato para outro: de EG "29 de outubro de 2005" a 2005-10-29. Eu tenho uma lista de 625 datas. Eu uso awk.
A conversão funciona - na maioria das vezes. Hovewer, às vezes a conversão não acontece, e a variável deve manter a data (convertida) permanece indefinida.
Isso sempre acontece com as mesmas linhas exatamente. Executando a `date 'explicitamente (do shell Bash) nas datas dessas linhas estranhas funciona bem (as datas são adequadamente convertidas). - Não é o conteúdo textual das linhas que importa.
Por que esse comportamento e como posso consertar meu script?
Ela é:
awk 'BEGIN { FS = "unused" } {
x = "undefined";
"date \"+%Y-%m-%d\" -d " $1 | getline x ;
print $1 " = " x
}' uBXr0r15.txt \
> bug-out-3.txt
Se você deseja reproduzir esse problema:
- Baixe este arquivo: ubxr0r15.txt.
- Execute o Skript Awk.
- Pesquise "indefinido" no bug-out-3.txt.
("indefinido" encontrado 122 vezes, no meu computador.)
Em seguida, você pode executar o script novamente e (no meu computador) Bug-Out-3.txt permanece inalterado-exatamente as mesmas datas são deixadas indefinidas.
(Gawk versão 3.1.6, Ubuntu 9.10.)
Atenciosamente, Magnus
Solução
Sempre que você abre um tubo ou arquivo para ler ou escrever em awk
, o último Vai primeiro verificar (usando um hash interno) se já tem um tubo ou arquivo com o mesmo nome (Ainda aberto; se for assim, Ele reutilizará o descritor de arquivo existente em vez de reabrir o tubo ou arquivo.
No seu caso, todas as entradas que acabam como undefined
são realmente duplicatas; a primeira vez que eles são encontrados (ou seja, quando o comando correspondente date "..." -d "..."
é emitido pela primeira vez) o resultado adequado é lido em x
. Nas ocorrências subsequentes da mesma data, getline
tentativas de ler uma segunda, terceira etc. linhas do original date
tubo, mesmo que o tubo tenha sido fechado por date
, resultando em x
não está mais sendo atribuído.
De gawk
Homem-página:
NOTA: Se estiver usando um tubo, co-processamento ou soquete para obter linha ou de impressão ou imprimir em um loop, você deve usar o fechamento () para criar novas instâncias do comando ou soquete. O AWK não fecha automaticamente tubos, soquetes ou co-processos quando retornam o EOF.
Você deve explicitamente close
o cano toda vez que você lê x
:
close("date \"+%Y-%m-%d\" -d " $1)
Aliás, seria bom sort
e uniq
uBXr0r15.txt
antes de se deparar awk
, ou você precisa do pedido/duplicação original?
Outras dicas
Embora eu ame awk, não é necessário para isso.
tr -d '"' < uBXr0r15.txt | date +%Y-%m-%d -f -
gawk 'BEGIN{
m=split("January|February|March|April|May|June|July|August|September|October|November|December",d,"|")
for(o=1;o<=m;o++){
months[d[o]]=sprintf("%02d",o)
}
FS="[, ]"
}
{
gsub(/["]/,"",$1)
gsub(/["]/,"",$4)
t=mktime($4" "months[$1]" "$2" 0 0 0")
print strftime("%Y-%m-%d",t)
}' uBXr0r15.txt
Fazer tudo dentro do Gawk será mais rápido do que chamar comandos externos.