使用 jq 就地修改 json 中的键值

Posted

技术标签:

【中文标题】使用 jq 就地修改 json 中的键值【英文标题】:Modify a key-value in a json using jq in-place 【发布时间】:2017-07-31 16:49:11 【问题描述】:

我有一个 json,我想在其中修改一个特定的值,但终端总是显示带有修改后的值的 json,但它实际上并没有改变特定文件中的值。示例 json:


   name: 'abcd',
   age: 30,
   address: 'abc'

我想更改文件本身的地址值,但到目前为止我一直无法这样做。我尝试使用:

jq '.address = "abcde"' test.json

但它没有用。有什么建议吗?

【问题讨论】:

这能回答你的问题吗? Jq to replace text directly on file (like sed -i) 【参考方案1】:

使用临时文件;这就是任何声称可以进行就地编辑的程序正在做的事情。

tmp=$(mktemp)
jq '.address = "abcde"' test.json > "$tmp" && mv "$tmp" test.json

如果地址不是硬编码的,则通过 jq 参数传递正确的地址:

address=abcde
jq --arg a "$address" '.address = $a' test.json > "$tmp" && mv "$tmp" test.json

【讨论】:

这适用于硬编码字符串。变量是否有任何解决方案,即 $address @ahmed_khan_89 你可以使用 jq '.address = "'$address'"' 不,不要将字符串插入jq 过滤器。请改用jq --arg a "$address" '.address = $a' @chepner 你怎么不推荐插入字符串?当我使用 Pujan 的方法时它可以工作 @AlexanderD 因为插值不一定将参数传递给您的过滤器;它构建一个过滤器,该过滤器取决于如何解析变量扩展。【参考方案2】:

AFAIK jq 不支持就地编辑,因此您必须先重定向到一个临时文件,然后用它替换您的原始文件,或者使用 moreutils 包中的 sponge 实用程序,如下所示:

jq '.address = "abcde"' test.json|sponge test.json

还有其他技术可以“重定向到同一个文件”,例如将输出保存在变量 e.t.c 中。如果您想了解更多相关信息,“Unix 和 Linux StackExchange”是一个很好的起点。

【讨论】:

不幸的是,CentOS 8 atm 上没有更多实用程序/海绵.. 没有sponge: echo "$( jq '.address = "abcde"' test.json )" > test.json 小心!将文件名作为参数传递给sponge,如答案所示。这是错误的:jq . test.json | sponge > test.json,你必须这样做jq . test.json | sponge test.json 另外点头让我看看海绵(1) 不要重定向到@codekandis 评论所建议的相同文件。这并不总是有效。大文件会导致问题,包括空格、不可打印和转义序列。永远不要将文件重定向到自身,这总是一个坏主意。查看海绵或使用临时文件,除非您了解正在发生的事情,否则不要尝试以不同的方式进行操作。【参考方案3】:

临时文件在不需要时会增加更多复杂性(除非您真正处理的 JSON 文件太大而无法将它们放入内存(GB 到 100 的 GB 或 TB,具体取决于您拥有多少 RAM/并行度)

纯粹的 bash 方式。

contents="$(jq '.address = "abcde"' test.json)" && \
echo "$contents" > test.json

优点

没有临时文件可以处理 纯 bash 不需要管理员安装sponge,默认不安装 更简单

注意:这不能组合为“一个命令”,因为重定向在运行表达式的左侧 (LHS) 之前 开始,并且在运行 jq 之前开始重定向会错误地清空文件,因此有两个单独的命令。

【讨论】:

这个答案对于诸如在你的 package.json 上修改版本(例如contents="$(jq '.version = "$version"' package.json)" && echo "$contents" > package.json)等事情特别有用【参考方案4】:

只是为了添加到 chepner 答案中,如果你想在 shell 脚本中使用它。

test.json


  "name": "abcd",
  "age": 30,
  "address": "abc"

脚本.sh

#!/bin/bash
address="abcde"
age=40

# Strings:
jq --arg a "$address" '.address = $a' test.json > "tmp" && mv "tmp" test.json

# Integers:
jq --argjson a "$age" '.age = $a' test.json > "tmp" && mv "tmp" test.json

【讨论】:

【参考方案5】:

具有更改单个值和多个值的嵌套 json 示例。

config.json


  "Parameters": 
    "Environment": "Prod",
    "InstanceType": "t2.micro",
    "AMIID": "ami-02d8e11",
    "ConfigRegion": "eu-west-1"
  

使用以下命令,您可以编辑多个值。

tmp=$(mktemp)
jq '.Parameters.AMIID = "ami-02d8sdfsdf" | .Parameters.Environment = "QA"' config.json > "$tmp" && mv "$tmp" config.json

使用以下命令,您可以编辑单个值。

tmp=$(mktemp)
jq '.Parameters.AMIID = "ami-02d8sdfsdf"' config.json > "$tmp" && mv "$tmp" config.json

【讨论】:

【参考方案6】:

这应该可以工作

address = aaaaa
echo $(jq --arg a "$address" '.address = ($a)' test.json) > test.json

无论出于何种原因,如果没有回显,它会生成一个 bin 文件,而我的 python 脚本无法解析它。

【讨论】:

在 shell 脚本中将结果流式传输到输入文件通常是有风险的:> 通常会导致在执行其余命令之前截断输出文件。可能子shell 命令$(...) 将其推迟到执行shell 脚本之后。一般来说,我总是使用命令行选项来处理这种特殊情况,或者缺少这种情况,写入中间文件。【参考方案7】:

我不喜欢任何解决方案并创建了sde utility。

pip install sde

那么你可以简单地做:

sde address abcde test.json

【讨论】:

以上是关于使用 jq 就地修改 json 中的键值的主要内容,如果未能解决你的问题,请参考以下文章

如何计算 JQ 中每个对象的键值对的出现次数?

如何获取自己键盘上按键的键值(KeyCode)

jq中获取object 的键值key

pig 新手,如何使用 pig 中的键值对子集将 JSON 转换为另一个 JSON?

PHP从多个json文件中的键值排序结果

在python中的JSON输出中引用特定的键值