jq:如何根据子字符串匹配更新值?

jq: how do I update a value based on a substring match?

我有一个 jq 问题。给定一个文件 file.json 包含:

[
  {
    "type": "A",
    "name": "name 1",
    "url": "http://domain.com/path/to/filenameA.zip"
  },
  {
    "type": "B",
    "name": "name 2",
    "url": "http://domain.com/otherpath/to/filenameB.zip"
  },
  {
    "type": "C",
    "name": "name 3",
    "url": "http://otherdomain.com/otherpath/to/filenameB.zip"
  }
]

我希望使用 jq 创建另一个文件,仅当 url 的值与某种模式匹配时才修改 url。例如,我想更新任何匹配模式的 url:

http://otherdomain.com.*filenameB.*

到一些固定的字符串,例如:

http://yetanotherdomain.com/new/path/to/filenameC.tar.gz

结果为 json:

[
  {
    "type": "A",
    "name": "name 1",
    "url": "http://domain.com/path/to/filenameA.zip"
  },
  {
    "type": "B",
    "name": "name 2",
    "url": "http://domain.com/otherpath/to/filenameB.zip"
  },
  {
    "type": "C",
    "name": "name 3",
    "url": "http://yetanotherdomain.com/new/path/to/filenameB.tar.gz"
  }
]

我连找到 url 都没走多远,更不用说更新了。这是我得到的(错误的结果,对我解决更新问题没有帮助):

% cat file.json | jq -r '.[] | select(.url | index("filenameB")).url'
http://domain.com/otherpath/to/filenameB.zip
http://otherdomain.com/otherpath/to/filenameB.zip
%

关于如何获取具有与正则表达式匹配的值的键的路径的任何想法?在那之后,如何用一些新的字符串值更新密钥?如果有多个匹配项,则应使用相同的新值更新所有匹配项。

好消息是这个问题有一个简单的解决方案:

map( if .url | test("http://otherdomain.com.*filenameB.*")
     then .url |= sub(  "http://otherdomain.com.*filenameB.*"; 
           "http://yetanotherdomain.com/new/path/to/filenameC.tar.gz")
     else .
     end)

不太好的消息是,除非您理解这里的关键技巧 - “|=”过滤器,否则解释起来并不那么容易。有很多关于它的 jq 文档,所以我只是指出它类似于 C 编程语言家族中的 += 运算符家族。

具体来说,.url |= sub(A;B) 类似于 .url = (.url|sub(A;B))。这就是更新的方式 "in-place".

这是一个解决方案,它使用 tostreamselect 识别 url 成员的路径,然后更新使用 reducesetpath

的值
  "http://otherdomain.com.*filenameB.*"                      as $from
| "http://yetanotherdomain.com/new/path/to/filenameC.tar.gz" as $to

| reduce (tostream | select(length == 2 and .[0][-1] == "url")) as $p (
      .
    ; setpath($p[0]; $p[1] | sub($from; $to))
  )