繁体   English   中英

将多个 jq 值传送到“while read -r”循环

[英]Pipe multiple jq values to “while read -r” loop

我正在尝试找到一种干净的方法,在 while 循环中将 2 个 json 字符串值分配给 2 个变量。 我正在使用的循环和输入如下。

输入:

foo='
[
   {
      "name":"name string (more info)",
      "nested_name":{
         "name":"my name",
         "conclusion":"failure",
         "number":11
      }
   },
   {
      "name":"name string (more info)",
      "nested_name":{
         "name":"my other name",
         "conclusion":"failure",
         "number":13
      }
   }
]'

目前我有以下几点:

echo "$foo" | jq ".[].name,.[].nested_name.name" | while read -r foo bar; do
    # do stuff with foo and bar
done

在迭代 1 中,我想要:

foo = "name string (more info)" 
bar = "my name"

迭代 2:

foo = "name string (more info)" 
bar = "my other name"

但是,这会生成以下不正确的输出:

foo = "name 
bar = string (more info)"
foo = "name 
bar = string (more info)"
foo = "my 
bar = name"
foo = "my 
bar = other name"

我整天都在这,任何意见或建议将不胜感激!

使用两个read ,每行一个:

#!/usr/bin/env bash

json='
[
   {
      "name":"name string (more info)",
      "nested_name":{
         "name":"my name",
         "conclusion":"failure",
         "number":11
      }
   },
   {
      "name":"name string (more info)",
      "nested_name":{
         "name":"my other name",
         "conclusion":"failure",
         "number":13
      }
   }
]'


while read -r foo && read -r bar; do
    printf "foo=%s\nbar=%s\n" "$foo" "$bar"
done < <(jq ".[] | .name, .nested_name.name" <<<"$json")

我不得不改变你的jq表达式以获得你想要的输出。

还要注意对原始 JSON 文本和循环变量使用不同的名称,使用here 字符串而不是echo ,并使用文件重定向而不是管道; 如果while的主体设置了以后需要的任何变量, while很有用。


将所有jq输出读入数组然后对其进行迭代的替代方法:

readarray -t lines < <(jq ".[] | .name, .nested_name.name" <<<"$json")
for (( i = 0; i < ${#lines[@]}; i += 2 )); do
    foo=${lines[i]}
    bar=${lines[i+1]}
    printf "foo=%s\nbar=%s\n" "$foo" "$bar"
done

如果我模拟一些“CSV 输入”,我可以这样做:(注意IFS设置为,

echo 'aaa,bbb\nccc,ddd' | while IFS=, read -r x y; do
  echo "x=$x, y=$y"
done

输出:

x=aaa, y=bbb
x=ccc, y=ddd

您可以使用以下jq命令生成类似的“CSV 输出”:

jq --raw-output '.[] | "\(.name),\(.nested_name.name)"'

所以我认为它看起来像这样:

echo "$foo" | jq --raw-output '.[] | "\(.name),\(.nested_name.name)"' | while IFS=, read -r foo bar; do
    # do stuff with foo and bar
done

这个怎么样: https : //jqplay.org/s/4MPWmMjFmM

assuming foobar.json
    jq -r '.[] | "foo=\"" + .name + "\"","bar=\"" + .nested_name.name + "\""' foobar.json
    foo="name string (more info)"
    bar="my name"
    foo="name string (more info)"
    bar="my other name"

我更喜欢这样做:

jq --argjson foo "$foo" --null-input --compact-output '$foo[]' | while IFS= read -r item
do
    name="$(jq --argjson item "$item" --null-input --raw-output '$item.name')"
    nested_name="$(jq --argjson item "$item" --null-input --raw-output '$item.nested_name.name')"
    printf 'name is "%s" and nested_name is "%s"\n' "$name" "$nested_name"
done

虽然此方法涉及对jq额外调用,但它有几个好处:

  • 如果您需要迭代具有更复杂结构的内容,则很容易扩展。
  • 它对空格、逗号、引号、制表符和换行符是安全的。
  • 简单易懂,有信心,不涉及任何特殊的算法,解析或转义。

如果您希望它更简洁,那么使用缩写选项也是如此。

jq --argjson foo "$foo" -nc '$foo[]' | while IFS= read -r item
do
    name="$(jq --argjson item "$item" -nr '$item.name')"
    nested_name="$(jq --argjson item "$item" -nr '$item.nested_name.name')"
    printf 'name is "%s" and nested_name is "%s"\n' "$name" "$nested_name"
done

更详细一点:

  • 我们使用--argjson将我们的 JSON 字符串作为单个参数--null-input并使用--null-input忽略标准输入。 这样我们就不必担心传入过程中的转义或分隔符。
  • 我们使用--compact-output (或-c )每行输出一个 JSON 对象。
  • 我们使用IFS=来确保我们的 JSON 字符串中的空格和制表符不会被当作分隔符。 只有换行符将用作分隔符,并且因为 JSON 字符串中的所有换行符都必须被转义,所以我们可以确信对于原始 JSON 数组中的每个项目,我们将只循环一次。
  • 我们使用--raw-output (或-r )将 JSON 字符串的值直接输出到 shell 变量中。 (如果我们想要 JSON 编码的字符串,我们可以选择省略它。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM