使用jq修改JSON

4

我希望使用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   } }

4
小建议:不要以 root 身份登录来进行工作。 - Kent
3个回答

5
你唯一的问题是传递给 jq 的脚本引号使用不正确。
在你的特定情况下,使用一个单双引号混合的字符串,并嵌入转义的 " 实例可能是最简单的。
jq -r ".global.name = \"$INPUT\"" "$CONF_FILE" > tmp.$$.json && mv tmp.$$.json "$CONF_FILE"

一般而言,然而chepner's helpful answer展示了一种更为强大的嵌入shell变量引用到脚本中的替代方案:使用--arg选项将值作为jq变量传递,允许在脚本中使用单引号,这是更可取的,因为它避免了关于哪些元素最先被shell扩展的混淆,并且省去了需要转义应该传递给jq$实例的麻烦。

另外:

  • 只使用=就足以赋值;而被称为更新运算符的|=也可以工作,但在这种情况下它的行为与=相同,因为RHS是一个字面量,而不是引用LHS的表达式 - 请参见手册
  • 你应该经常双引号引用你的shell变量引用,并且应该避免使用全大写变量名来避免与环境变量和特殊shell变量发生冲突

关于为什么你的引用没有起作用的问题: '.global.name |= '""$INPUT""由以下标记组成:
- 字符串文字.global.name |=(由单引号引起来) - 字符串文字"",即空字符串 - 引号将在shell在jq看到脚本之前被删除 - 对变量$INPUT的未加引号的引用(使其值受到分词和通配符扩展的影响) - 另一个""字面量。
对于你的样本值,jq最终看到以下字符串作为其脚本:
.global.name |= dsa

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


5

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

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

这确保了精确的$INPUT值被用作JSON值并进行引用。


1

使用简单的过滤器和 jq 应该可以为您完成。

.global.name = "dsa"

i.e.

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

您可以在此处使用json-filters进行操作,点击这里


1
“|=” 不是问题,引用才是。 - chepner
@chepner:之前没听清楚。 - Inian
2
请注意,_filter_是jq中非常广泛的概念(引用自手册:“jq程序是一个“过滤器”:它接受输入并产生输出。”),而此处感兴趣的特性是 _assignment_。 - mklement0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接