使用 jq 和 bash 为数组中的每个对象运行命令

Posted

技术标签:

【中文标题】使用 jq 和 bash 为数组中的每个对象运行命令【英文标题】:Using jq with bash to run command for each object in array 【发布时间】:2017-08-28 18:54:03 【问题描述】:

如何使用jq 为 JSON 数组中的每个 JSON 对象运行 Bash 命令?到目前为止,我有这个:

cat credentials.json | jq -r '.[] | .user, .date, .email' | mycommand -u user -d date -e email

这似乎不起作用。如何将参数从 JSON 数组中取出到我的命令中?

我的 JSON 文件如下所示:

[
   "user": "danielrvt",
   "date": "11/10/1988",
   "email": "myemail@domain.com",
   ...
]

【问题讨论】:

【参考方案1】:

您最好的选择可能是以 TSV 格式输出每条记录,然后从 shell 循环中读取。

jq -r '.[]|[.user, .date, .email] | @tsv' |
  while IFS=$'\t' read -r user date email; do
    mycommand -u "$user" -d "$date" -e "$email"
  done

jq 本身没有类似system 调用来从过滤器中运行外部命令,尽管看起来they are working on it。

【讨论】:

【参考方案2】:

使用 xargs:

curl localhost:8082/connectors | jq .[] | xargs -L1 -I'' curl -XDELETE 'localhost:8082/connectors/' 

或者等效地,显示第一个 curl 的输出:

echo '["quickstart-file-sink4","quickstart-file-source","quickstart-file-sink","quickstart-file-sink2","quickstart-file-sink3","quickstart-file-source2"]' | jq .[] | xargs -L1 -I'' curl -XDELETE 'localhost:8082/connectors/' 

jq .[] 剥离了一层包含,因此一个列表作为每个项目一行输出。

xargs -L1 一次处理一行

xargs -I'' 指定在调用以下命令时将字符串 替换为输入行。

xargs 本质上是 shell 的映射运算符。

【讨论】:

这是遍历值数组并为每个值执行命令的绝佳选择。它不需要多行脚本,也不需要生成命令的步骤和执行命令的步骤。 xargs 是一个很棒的命令。 我一个字母一个字母地复制它,但它不起作用:curl: (3) URL 位置 28 大括号内的空字符串:localhost:8082/connectors/ 执行简单的“xargs echo”时jq 的输出将打印,但永远不会使用替换令牌:( 我在使用管道xargs 命令时遇到问题,如答案所示,但这个答案肯定让我走上了正确的道路!对于那些感兴趣的人,我最终用更详细的配置标志版本替换了参数,这个问题的等价物是curl localhost:8082/connectors | jq .[] | xargs --max-lines=1 --replace= curl -XDELETE 'localhost:8082/connectors/'(我还在链接之前使用了jq的-r标志)。【参考方案3】:

您可以让jq 输出要执行的命令,例如

.[] | "mycommand \(.user|@sh) \(.date|@sh) \(.email|@sh)"

然后执行它。类似的东西

bash <(jq -r '.[] | "mycommand \(.user|@sh) \(.date|@sh) \(.email|@sh)"' foo)

【讨论】:

(.propname) 是我所需要的。 |@sh 是管道每个单独的属性吗?这与 (.user) 有何不同 @sh 保证每个参数都被正确引用,因此如果这是输出的话,shell 不会分词或扩展 glob。假设有人设法删除了像 "email": "daniel;rm -rf *", ... 这样的文件【参考方案4】:

这是我基于@chepner 的回答的另一个变体。

echo "$config" | jq -c '.[]' |
while IFS=$"\n" read -r c; do
    echo "start"
    host=$(echo "$c" | jq -r '.host')
    echo $host
    echo "end"
done

我使用 jq 的 -c 选项来输出“紧凑”的 json,所以它们都在一行上。

结合IFS=$"\n",我能够遍历输入json数组中的每个项目并做我想做的事情。

所以,输入

[
 
  "host": "host1",
  "settings": 
 ,
 
  "host": "host1",
  "settings": 
 
]

输出是

start
host1
end
start
host2
end

【讨论】:

【参考方案5】:

我最近遇到了同样的问题,由于我想传递的参数集相对复杂,xargs 并没有太大帮助。因此,我为 jq 实现了一个 sh 过滤器(及其朋友)。我还没有足够的时间为它编写文档和测试,所以还没有创建 PR 让它成为官方代码库的一部分。所以现在只针对愿意自己编译这个版本的人:

https://github.com/haochenx/jq/tree/sh-support

【讨论】:

我查看了您的代码。我喜欢这个主意。您添加到 src/builtin.c 的一些代码是重复的,需要简化,但我认为您应该针对 stedolan/jq 打开拉取请求。

以上是关于使用 jq 和 bash 为数组中的每个对象运行命令的主要内容,如果未能解决你的问题,请参考以下文章

使用 jq 为 JSON 对象的嵌套数组中的属性展平数组

如何使用jq将两个文件中的数组合并到一个数组中?

argparse 处理 bash 命令中的字符串和空格

如何将bash脚本中的json解析为数组?数组值应该同时具有 key:value 格式

如何使用jq根据内部数组中的值过滤对象数组?

获取jq中的对象数组索引