【问题标题】:jq - can't append objects to array in while loopjq - 不能在while循环中将对象附加到数组
【发布时间】:2020-08-27 10:37:38
【问题描述】:

我有这个带有服务帐户的 service-accounts-list.txt 文件:

serviceAccount:p12345-001@gcp-sa-logging.iam.gserviceaccount.com
serviceAccount:p12345-002@gcp-sa-logging.iam.gserviceaccount.com
serviceAccount:p12345-003@gcp-sa-logging.iam.gserviceaccount.com
serviceAccount:p12345-004@gcp-sa-logging.iam.gserviceaccount.com
serviceAccount:p12345-005@gcp-sa-logging.iam.gserviceaccount.com

还有这个我想将数据附加到的results.json 文件:

{
  "access": [
    {
      "role": "WRITER",
      "specialGroup": "projectWriters"
    },
    {
      "role": "OWNER",
      "specialGroup": "projectOwners"
    },
    {
      "role": "READER",
      "specialGroup": "projectReaders"
    }
  ]
}

预期结果:我想为service-accounts-list.txt 中的每一行添加一个对象,如下所示:

{
  "access": [
    {
      "role": "WRITER",
      "specialGroup": "projectWriters"
    },
    {
      "role": "OWNER",
      "specialGroup": "projectOwners"
    },
    {
      "role": "READER",
      "specialGroup": "projectReaders"
    },
    {
      "role": "WRITER",
      "userByEmail": "serviceAccount:p12345-001@gcp-sa-logging.iam.gserviceaccount.com"
    },
    {
      "role": "WRITER",
      "userByEmail": "serviceAccount:p12345-002@gcp-sa-logging.iam.gserviceaccount.com"
    },
    {
      "role": "WRITER",
      "userByEmail": "serviceAccount:p12345-003@gcp-sa-logging.iam.gserviceaccount.com"
    },
    {
      "role": "WRITER",
      "userByEmail": "serviceAccount:p12345-004@gcp-sa-logging.iam.gserviceaccount.com"
    },
    {
      "role": "WRITER",
      "userByEmail": "serviceAccount:p12345-005@gcp-sa-logging.iam.gserviceaccount.com"
    }
  ]
}

到目前为止,我的 bash 脚本如下所示:

while read p;
do
  cat result.json | jq --arg email "$p" '.access += [{"role": "WRITER", "userByEmail": $email}]' > result.json
done < service-accounts-list.txt

生成的result.json 文件实际上是空的。如果我将输出重定向到example &gt; result2.json,它会正确添加最后一个服务帐户,如下所示:

{
  "access": [
    {
      "role": "WRITER",
      "specialGroup": "projectWriters"
    },
    {
      "role": "OWNER",
      "specialGroup": "projectOwners"
    },
    {
      "role": "READER",
      "specialGroup": "projectReaders"
    },
    {
      "role": "WRITER",
      "userByEmail": "serviceAccount:p12345-005@gcp-sa-logging.iam.gserviceaccount.com"
    }
  ]
}

所以看来jq 语法是正确的。我尝试添加 --unbuffered 标志无济于事。

我错过了什么?

【问题讨论】:

  • 为了您的方便,ShellCheck自动检测this等常见问题。
  • 非常感谢您的提示。这对于像我这样的 bash 脚本新手很有用

标签: json bash append jq


【解决方案1】:

虽然明显的问题是您正在读取并重定向到同一文件,而这在 bashthis answer has many useful explanations and work around for that 中是无法做到的。

但更好的解决方案是完全避免在循环中多次调用jq,并在一次执行jq 时完成。

# array to hold new values in json format
narr=()

# loop through input file and append json formatted values to array
while read -r line; do
    narr+=('{"role": "WRITER", "userByEmail": "'$line'"}')
done < service-accounts-list.txt

# call jq only once using -n option
jq -n 'input | .access += [inputs]' results.json <(printf '%s\n' "${narr[@]}")

输出:

{
  "access": [
    {
      "role": "WRITER",
      "specialGroup": "projectWriters"
    },
    {
      "role": "OWNER",
      "specialGroup": "projectOwners"
    },
    {
      "role": "READER",
      "specialGroup": "projectReaders"
    },
    {
      "role": "WRITER",
      "userByEmail": "serviceAccount:p12345-001@gcp-sa-logging.iam.gserviceaccount.com"
    },
    {
      "role": "WRITER",
      "userByEmail": "serviceAccount:p12345-002@gcp-sa-logging.iam.gserviceaccount.com"
    },
    {
      "role": "WRITER",
      "userByEmail": "serviceAccount:p12345-003@gcp-sa-logging.iam.gserviceaccount.com"
    },
    {
      "role": "WRITER",
      "userByEmail": "serviceAccount:p12345-004@gcp-sa-logging.iam.gserviceaccount.com"
    },
    {
      "role": "WRITER",
      "userByEmail": "serviceAccount:p12345-005@gcp-sa-logging.iam.gserviceaccount.com"
    }
  ]
}

【讨论】:

    【解决方案2】:

    您可以使用jq 本身而无需任何shell 处理。使用原始输入模式-R 在空输入上处理纯文本文件,并从头开始形成结果 JSON。

    reduce() 函数迭代地运行文本文件中的每个条目,并创建一个具有角色名称的对象并将其附加到 access 数组中。

    jq -Rn \
       --slurpfile res result.json \
       '[ inputs | split("\n") | add ] as $data 
        | $res[] 
        | reduce $data[] as $d (.; .access += [{ role: "WRITER", userByEmail: $d  }] )' \
    service-accounts-list.txt
    

    【讨论】:

      【解决方案3】:

      也许最简单有效的解决方案是:

      jq -nR --argfile result result.json '
        $result 
        | .access += [{role: "WRITER", userByEmail: inputs}] 
      ' service-accounts-list.txt
      

      注意 -- 没有 slurping、没有拆分、没有 shell 脚本。如果使用--argfile 让您感到困扰,请随意使用--slurpfile,但可以写$result[0] 来“取消”$result。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-07-06
        • 2016-02-03
        • 2017-02-11
        • 1970-01-01
        • 2018-04-17
        • 2019-01-11
        • 1970-01-01
        相关资源
        最近更新 更多