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

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

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

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

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

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

【问题讨论】:

标签: json bash shell jq


【解决方案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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-01-08
        相关资源
        最近更新 更多