Como faço para invocar jq dentro do awk?

2

Essencialmente eu tenho um arquivo.log como segue

blah blah
blah blah
Hello world | {"foo": "bar"}
blah blah
Hello earth | {"foo1": "bar1"}

Agora, meu objetivo é escrever alguns comandos de shell para ter uma saída de desejo como esta:

Hello earth | "bar"
Hello earth | "bar1"

Atualmente, isso é o que eu tenho:

grep Hello file.log | awk -F "|" '{print $1, system("jq " $2)}'

No entanto, chamar jq está me dando este erro:

jq: error: syntax error, unexpected ':', expecting $end (Unix shell quoting issues?) at <top-level>, line 1:
bin:application   
jq: 1 compile error

Estou pensando que é porque dentro do sistema (), meus $ 12 são retirados de todo o caractere de aspas ("), portanto, o JQ não reconhece o seu json. Alguma sugestão?

    
por Luong Ngoc Son 30.09.2017 / 12:20

2 respostas

1

Você tem vários problemas aqui

  • system não retorna algo para imprimir, ele retorna o valor de saída do comando que você executou (0 se tudo correu com multas). Você verá seus dados decodificados em JSON e, em seguida, uma linha como Hello earth 0
  • as aspas duplas na string JSON são engolidas pelo shell. O comando resultante que você está executando é jq {foo: bar} (dois argumentos, JSON não são mais citados)
  • se $2 contiver caracteres especiais como $ , seu shell os interpretará
  • mesmo com a cotação correta, jq não é chamado assim, espera um filtro como primeiro argumento (digamos ' . ') e espera que a entrada JSON seja lida de um arquivo ou da entrada padrão
  • criar um comando a partir dos logs e executá-lo tem uma enorme implicação de segurança (e se $2 fosse ; rm -rf ~ ?). É melhor evitá-lo, se puder.

O problema de segurança reservado, aqui está um código awk que funcionará na maioria das vezes:

awk -F "|" '{ printf "%s", $1; system("echo \x27" $2 "\x27 | jq .")}'

O que ele faz é enviar $2 entre aspas simples ( \x27 ) para jq através de stdin.

Problemas permanecem, embora

  • se $2 contiver uma aspa simples, ele quebrará o comando inteiro
  • se $2 começar com um traço (improvável), ele será interpretado como uma opção para echo (podemos usar o comando printf em vez de echo )
  • o problema de segurança já mencionado (por exemplo, se $2 contiver ...'; rm -r ~; : ' ... em qualquer lugar da string)

Agora, um melhor awk code

awk -F "|" '{ printf "%s", $1; print $2 | "jq ."; close("jq ."); }'

Como $2 é enviado para um processo jq através de stdin, mas agora usando um awk pipe, ele não é mais interpretado pelo shell, resolvendo todos os problemas acima. O comando jq deve ser fechado (terminado) em cada linha, portanto, a chamada para close() .

    
por 30.09.2017 / 15:27
0

xhienne deu uma boa visão geral dos problemas com o código existente, e uma boa alternativa para o que você quer para realizar.

O seguinte é outra alternativa: não tente chamar jq de awk , mas deixe o script awk criar uma saída JSON adequada.

$ awk -F '|' 'BEGIN { print "[" } $2 { if (t) print t ","; t = $2 } END { print t, "]" }' file |jq .
[
  {
    "foo": "bar"
  },
  {
    "foo1": "bar1"
  }
]

O código awk , por si só, gerará o seguinte array JSON dos objetos JSON encontrados (dado o exemplo na pergunta):

[
 {"foo": "bar"},
 {"foo1": "bar1"} ]

Isso permite que você trabalhe mais livremente com jq sem tornar seu script muito difícil de manter e entender.

O malabarismo com a variável t no script é apenas uma maneira de garantir que não recebemos uma vírgula final após o último objeto JSON.

    
por 30.09.2017 / 15:59