如何在 awk 中调用 jq?

Luo*_*Son 3 grep awk quoting jq

基本上我有一个 file.log 如下

blah blah
blah blah
Hello world | {"foo": "bar"}
blah blah
Hello earth | {"foo1": "bar1"}
Run Code Online (Sandbox Code Playgroud)

现在我的目标是编写一些 shell 命令来获得如下输出:

Hello earth | "bar"
Hello earth | "bar1"
Run Code Online (Sandbox Code Playgroud)

目前这是我所拥有的:

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

但是调用 jq 给了我这个错误:

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

我想是因为在 system() 里面,我的 $12 被去掉了所有的引号字符 (") 因此 JQ 无法识别它的 json。有什么建议吗?

xhi*_*nne 5

你在这里有几个问题

  • system不返回要打印的内容,它返回您执行的命令的退出值(如果一切正常,则为 0)。您将看到您的 JSON 解码数据,然后是一行Hello earth 0
  • JSON 字符串中的双引号被 shell 吞掉了。您正在执行的结果命令是jq {foo: bar}(两个参数,不再引用 JSON)
  • 如果$2包含特殊字符,如$,您的外壳将解释它们
  • 即使使用正确的引用,jq也不会这样调用,它需要一个过滤器作为第一个参数(比如“ .”),并且它需要从文件或标准输入中读取 JSON 输入
  • 从日志构建命令并执行它具有巨大的安全意义(如果$2; rm -rf ~?)。如果可以,最好避免它。

撇开安全问题不谈,这里是一个awk大部分时间都可以工作的代码:

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

它的作用是将$2括在单引号 ( \x27) 中的内容发送jq到标准输入。

问题依然存在

  • 如果$2包含单引号,它将破坏整个命令
  • 如果$2以破折号开头(不太可能),它将被解释为一个选项echo(我们可以使用printf命令而不是echo
  • 已经提到的安全问题(例如,如果$2包含...'; rm -r ~; : ' ...字符串中的任何位置)

现在有更好的awk代码

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

由于通过 stdin$2发送到jq进程,但现在使用awk管道,它不再被 shell 解释,解决了上述所有问题。该jq命令必须在每一行关闭(终止),因此调用close().