【问题标题】:Pipe multiple jq values to "while read -r" loop通过管道将多个 jq 值传递给“while read -r”循环
【发布时间】:2021-10-03 17:28:32
【问题描述】:

我正在尝试找到一种干净的方法来将 2 个 json 字符串值分配给 while 循环中的 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"

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

【问题讨论】:

    标签: json bash while-loop jq ifs


    【解决方案1】:

    如果我模拟一些“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
    

    【讨论】:

    • 如果其中一个名称字符串中有逗号怎么办?
    • @Shawn Yep fair。我期待评论;)在这种情况下确实行不通。如果它没有机会发生,那么它可能是也可能不是一个体面的解决方法。
    【解决方案2】:

    使用两个reads,每行一个:

    #!/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 string 而不是 echo,并使用文件重定向而不是管道;如果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
    

    【讨论】:

    • 谢谢!我将您的答案与其他人结合起来,以获得我需要的确切行为。第一次在这里发帖,我对回复的质量和及时性感到震惊,谢谢!
    【解决方案3】:

    这个怎么样: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"
    

    【讨论】:

    • 哇,非常感谢!我使用另一个人的答案将它传送到一个 while -r 循环,并得到了我需要的东西!第一次在这里发布问题,我被震撼了:)
    • 逗号的意思是,它每次都会对数据进行新的运行并将它们全部粘贴在一起,如下所示:jqplay.org/s/mL_23Tl6Tr
    【解决方案4】:

    我更喜欢这样:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-03-27
      • 2013-07-18
      • 1970-01-01
      • 2012-08-28
      • 1970-01-01
      • 1970-01-01
      • 2021-12-27
      • 2018-01-25
      相关资源
      最近更新 更多