使用 jq 修改 JSON

Modifying JSON by using jq

我想使用 Linux 命令行修改 JSON 文件。

我尝试了这些步骤:

[root@localhost]# INPUT="dsa"
[root@localhost]# echo $INPUT
dsa
[root@localhost]# CONF_FILE=test.json
[root@localhost]# echo $CONF_FILE
test.json
[root@localhost]# cat $CONF_FILE
{
  "global" : {
    "name" : "asd",
    "id" : 1
  }
}
[root@localhost]# jq -r '.global.name |= '""$INPUT"" $CONF_FILE > tmp.$$.json && mv tmp.$$.json $CONF_FILE
jq: error: dsa/0 is not defined at <top-level>, line 1:
.global.name |= dsa
jq: 1 compile error

期望的输出:

[root@localhost]# cat $CONF_FILE
    {   "global" : {
    "name" : "dsa",
    "id" : 1   } }

jq 与直接过滤器一起使用,应该为您完成。

.global.name = "dsa"

jq '.global.name = "dsa"' json-file
{
  "global": {
    "name": "dsa",
    "id": 1
  }
}

您可以使用 json 过滤器,here

您唯一的问题是传递给 jq 的脚本引用不正确。

在您的特定情况下,使用带有嵌入式 \-转义 " 实例的单个双引号字符串可能是最简单的:

jq -r ".global.name = \"$INPUT\"" "$CONF_FILE" > tmp.$$.json && mv tmp.$$.json "$CONF_FILE"

但是,一般来说, 显示了一种比直接在脚本中嵌入 shell 变量引用更可靠的替代方法:使用 --arg 选项 将值作为 jq 变量 允许 单引号 脚本,这是更可取的,因为它避免了混淆 shell 预先扩展了哪些元素,并且避免了转义应该传递给 jq.[=38 的 $ 实例的需要=]

还有:

  • 只需=即可赋值;虽然 |=,即所谓的更新运算符,也可以工作,但在这种情况下它的行为与 = 相同,因为 RHS 是 文字 ,而不是引用 LHS 的表达式 - 参见 the manual.
  • 您应该经常用双引号引用您的 shell 变量引用,并且您应该避免使用全部大写的变量名称以便 avoid conflicts with environment variables and special shell variables.

至于为什么你的引用不起作用

'.global.name |= '""$INPUT"" 由以下标记组成:

  • 字符串文字 .global.name |=(由于单引号)
  • 字符串文字 "" - 即空字符串 - 在 jq 看到脚本之前,shell 会 删除 引号
  • 对变量 $INPUT 未引用 引用(这使得它的值受到分词和通配的影响)。
  • 文字的另一个实例""

根据您的示例值,jq 最终看到以下字符串作为其脚本:

.global.name |= dsa

如您所见,缺少双引号,导致 jqdsa 解释为 函数名称 而不是字符串文字,并且由于没有参数传递给(不存在的)函数 dsajq 的错误消息将其引用为 dsa/0 - 一个没有(0)参数的函数。

使用--arg选项传递值更简单和安全:

jq -r --arg newname "$INPUT" '.global.name |= $newname' "$CONF_FILE"

这确保 $INPUT 的准确值被使用并引用为 JSON 值。