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".
这是一个解决方案,它使用 tostream 和 select 识别 url 成员的路径,然后更新使用 reduce 和 setpath
的值
"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))
)
我有一个 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".
这是一个解决方案,它使用 tostream 和 select 识别 url 成员的路径,然后更新使用 reduce 和 setpath
的值 "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))
)