bash Jq 直接替换文件上的文本(如 sed -i)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36565295/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 22:14:17  来源:igfitidea点击:

Jq to replace text directly on file (like sed -i)

bashjq

提问by Supra

I have a json file that needs to be updated on a certain condition.

我有一个需要在特定条件下更新的 json 文件。

Sample json

示例 json

{
   "Actions" : [
      {
         "value" : "1",
         "properties" : {
            "name" : "abc",
            "age" : "2",
            "other ": "test1"
          }
      },
      {
         "value" : "2",
         "properties" : {
            "name" : "def",
            "age" : "3",
            "other" : "test2"
          }
      }
   ]
}

I am writing a script that makes use of Jq to match a value and update, as shown below

我正在编写一个脚本,它利用 Jq 来匹配值和更新,如下所示

cat sample.json |  jq '.Actions[] | select (.properties.age == "3") .properties.other = "no-test"'

Output (printed to terminal)

输出(打印到终端)

{
  "value": "1",
  "properties": {
    "name": "abc",
    "age": "2",
    "other ": "test1"
  }
}
{
  "value": "2",
  "properties": {
    "name": "def",
    "age": "3",
    "other": "no-test"
  }
}

While this command makes the needed change, it outputs the entire json on the terminal and does not make change to the file itself.

虽然此命令进行了所需的更改,但它会在终端上输出整个 json,并且不会更改文件本身。

Please advise if there is an option to have jq make changes on the file directly (similar to sed -i).

请告知是否可以选择让 jq 直接对文件进行更改(类似于 sed -i)。

回答by peak

This post addresses the question about the absence of the equivalent of sed's "-i" option, and in particular the situation described:

这篇文章解决了缺少 sed 的“-i”选项等价物的问题,特别是描述的情况:

I have a bunch of files and writing each one to a separate file wouldn't be easy.

我有一堆文件,将每个文件写入一个单独的文件并不容易。

There are several options, at least if you are working in a Mac or Linux or similar environment. Their pros and cons are discussed at http://backreference.org/2011/01/29/in-place-editing-of-files/so I'll focus on just three techniques:

有多种选择,至少如果您在 Mac 或 Linux 或类似环境中工作。它们的优缺点在http://backreference.org/2011/01/29/in-place-editing-of-files/中讨论, 所以我将只关注三种技术:

One is simply to use "&&" along the lines of:

一种是简单地使用“&&”,如下所示:

jq ... INPUT > INPUT.tmp && mv INPUT.tmp INPUT

Another is to use the spongeutility (part of GNU moreutils):

另一个是使用该sponge实用程序(GNU 的一部分moreutils):

jq ... INPUT | sponge INPUT

The third option might be useful if it is advantageous to avoid updating a file if there are no changes to it. Here is a script which illustrates such a function:

如果有利于避免在没有更改的情况下更新文件,则第三个选项可能很有用。这是一个说明此类功能的脚本:

#!/bin/bash

function maybeupdate {
    local f=""
    cmp -s "$f" "$f.tmp"
    if [ $? = 0 ] ; then
      /bin/rm $f.tmp
    else
      /bin/mv "$f.tmp" "$f"
    fi
}

for f
do
    jq . "$f" > "$f.tmp"
    maybeupdate "$f"
done

回答by Jeff Mercado

You'll want to update the action objects without changing the context. By having the pipe there, you're changing the context to each individual action. You can control that with some parentheses.

您将希望在不更改上下文的情况下更新操作对象。通过在那里放置管道,您可以更改每个单独操作的上下文。你可以用一些括号来控制它。

$ jq --arg age "3" \
'(.Actions[] | select(.properties.age == $age).properties.other) = "no-test"' sample.json

This should yield:

这应该产生:

{
  "Actions": [
    {
      "value": "1",
      "properties": {
        "name": "abc",
        "age": "2",
        "other ": "test1"
      }
    },
    {
      "value": "2",
      "properties": {
        "name": "def",
        "age": "3",
        "other": "no-test"
      }
    }
  ]
}

You can redirect the results to a file to replace the input file. It won't do in-place updates to a file as sed does.

您可以将结果重定向到文件以替换输入文件。它不会像 sed 那样对文件进行就地更新。

回答by moriaki

instead of sponge:

而不是sponge

cat <<< $(jq 'QUERY' sample.json) > sample.json

回答by Charles Merriam

You ran into two issues:

你遇到了两个问题:

  • This is a common problem for text processing, not solved in the base Linux distribution.
  • jqdid not write special code to overcome this problem.
  • 这是文本处理的常见问题,在基本 Linux 发行版中没有解决。
  • jq没有写特殊的代码来克服这个问题。

One good solution:

一个很好的解决方案:

  • Install moreutilsusing brew install moreutilsor your favorite package manager. This contains the handy program sponge, for just this purpose.
  • Use cat myfile | jq blahblahblah | sponge myfile. That is, run jq, capturing the standard out, when jqhas finished, then write the standard output over myfile(the input file).
  • 使用或您最喜欢的包管理器安装moreutilsbrew install moreutils。这包含方便的程序sponge,仅用于此目的。
  • 使用cat myfile | jq blahblahblah | sponge myfile. 也就是说,运行jq,捕获标准输出,当jq完成后,然后将标准输出myfile(输入文件)写入。

回答by Will Barnwell

Using my answer to a duplicate question

使用我对重复问题的回答

Assignment prints the whole object with the assignment executed so you could assign a new value to .Actionsof the modified Actions array

.Actions=([.Actions[] | if .properties.age == "3" then .properties.other = "no-test" else . end])

I used an if statement but we can use your code to do the same thing

.Actions=[.Actions[] | select (.properties.age == "3").properties.other = "no-test"]

赋值打印执行赋值的整个对象,以便您可以.Actions为修改后的 Actions 数组分配一个新值

.Actions=([.Actions[] | if .properties.age == "3" then .properties.other = "no-test" else . end])

我使用了 if 语句,但我们可以使用您的代码来做同样的事情

.Actions=[.Actions[] | select (.properties.age == "3").properties.other = "no-test"]

The above will output the entire json with .Actionsedited. jq does not had sed -ilike functionality, but all you need to do is pipe it back into a spongeto the file with | sponge

以上将输出整个 json 并.Actions编辑。jq 没有sed -i类似的功能,但您需要做的就是将它用管道送回海绵文件中| sponge

 jq '.Actions=([.Actions[] | if .properties.age == "3" then .properties.other = "no-test" else . end])' sample.json | sponge sample.json