简体   繁体   English

如何根据输入用jq替换JSON中的字符串

[英]How to substitute strings in JSON with jq based on the input

Given the input in this form 以这种形式输入

[
  {
    "DIR" : "/foo/bar/a/b/c",
    "OUT" : "/foo/bar/x/y/z",
    "ARG" : [ "aaa", "bbb", "/foo/bar/a", "BASE=/foo/bar" ]
  },
  {
    "DIR" : "/foo/baz/d/e/f",
    "OUT" : "/foo/baz/x/y/z",
    "ARG" : [ "ccc", "ddd", "/foo/baz/b", "BASE=/foo/baz" ]
  },
  { 
    "foo" : "bar"
  }
]

I'm trying to find out how to make jq transform that into this: 我试图找出如何使jq转换成这样:

[
  {
    "DIR" : "BASE/a/b/c",
    "OUT" : "BASE/x/y/z",
    "ARG" : [ "aaa", "bbb", "BASE/a", "BASE=/foo/bar" ]
  },

  {
    "DIR" : "BASE/d/e/f",
    "OUT" : "BASE/x/y/z",
    "ARG" : [ "ccc", "ddd", "BASE/b", "BASE=/foo/baz" ]
  },
  { 
    "foo" : "bar"
  }
]

In other words, objects having an "ARG" array, containing a string that starts with "BASE=" should use the string after "BASE=" , eg "/foo" to substitute other string values that start with " /foo" (except the "BASE=/foo" which should remain unchanged") 换句话说,具有"ARG"数组且包含以"BASE="开头的字符串的对象应使用"BASE="之后的字符串,例如"/foo"来替换以“ /foo"开头的其他字符串值(除外, "BASE=/foo"应保持不变”)

I'm not even close to finding a solution myself, and at this point I'm unsure that jq alone will do the job. 我什至无法自己找到解决方案,在这一点上,我不确定仅jq就能胜任。

Don't worry, jq alone will do the job: 不用担心,仅jq就能完成工作:

jq 'def sub_base($base): if (startswith("BASE") | not) then sub($base; "BASE") else . end;
    map(if .["ARG"] then ((.ARG[] | select(startswith("BASE=")) | split("=")[1]) as $base
            | to_entries
            | map(if (.value | type == "string") then .value |= sub_base($base)
                  else .value |= map(sub_base($base)) end)
            | from_entries)
        else . end)' input.json

The output: 输出:

[
  {
    "DIR": "BASE/a/b/c",
    "OUT": "BASE/x/y/z",
    "ARG": [
      "aaa",
      "bbb",
      "BASE/a",
      "BASE=/foo/bar"
    ]
  },
  {
    "DIR": "BASE/d/e/f",
    "OUT": "BASE/x/y/z",
    "ARG": [
      "ccc",
      "ddd",
      "BASE/b",
      "BASE=/foo/baz"
    ]
  },
  {
    "foo": "bar"
  }
]

With jq : jq

#!/usr/bin/jq -f

# fix-base.jq
def fix_base:
  (.ARG[] | select(startswith("BASE=")) | split("=")[1]) as $base
  | .DIR?|="BASE"+ltrimstr($base)
  | .OUT?|="BASE"+ltrimstr($base)
  | .ARG|=map(if startswith($base) then "BASE"+ltrimstr($base) else . end)
  ;

map(if .ARG? then fix_base  else . end)

You can run it like this: 您可以这样运行它:

jq -f fix-base.jq input.json

or make it an executable like this: 或使其像这样的可执行文件:

chmod +x fix-base.jq
./fix-base.jq input.json

Some helper functions make the going much easier. 一些帮助程序功能使操作变得更加容易。 The first is generic and worthy perhaps of your standard library: 第一个是通用的,也许值得您使用的标准库:

# Returns the integer index, $i, corresponding to the first element
# at which f is truthy, else null
def indexof(f):
  label $out
  | foreach .[] as $x (null; .+1; 
      if ($x|f) then (.-1, break $out) else empty end) // null;

# Change the string $base to BASE using gsub
def munge($base):
  if type == "string" and (test("^BASE=")|not) then gsub($base; "BASE")
  elif type=="array" then map(munge($base))
  elif type=="object" then map_values(munge($base))
  else .
  end;

And now the easy part: 现在,最简单的部分是:

map(if has("ARG") 
    then (.ARG|indexof(test("^BASE="))) as $ix
    | if $ix
      then (.ARG[$ix]|sub("^BASE=";"")) as $base | munge($base)
      else . end 
    else . end )

Some points to note: 需要注意的几点:

  • You may wish to use sub rather than gsub in munge ; 您可能希望在munge使用sub而不是gsub
  • The above solution assumes you want to make the change in all keys, not just "DIR", "OUT", and "ARG" 上述解决方案假定您要更改所有键,而不仅仅是“ DIR”,“ OUT”和“ ARG”
  • The above solution allows specifications of BASE that include one or more occurrences of "=". 上述解决方案允许BASE规范包含一个或多个出现的“ =”。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM