正则表达式(regex)是用于指定文本搜索模式的字符序列。
学习正则表达式的语法超出了本主题的范围。我们将重点介绍jq提供的、用于使用正则表达式的那些表达式。
不同的工具实现了不同版本的正则表达式。jq内置了 Oniguruma 正则表达式库,它与 Perl v5.8 的正则表达式基本兼容。
jq使用的具体语法可以在 Oniguruma 的 GitHub 仓库中找到。
jq没有为正则表达式提供任何特殊语法。它们只是以字符串的形式表示。这意味着正则表达式中的任何反斜杠都需要在字符串中转义。
例如,数字字符类(\d)必须写成"\\d"。
jq中的正则表达式仅限于一组过滤器。
当你需要知道一个字符串是否匹配某个模式时,使用test过滤器。
STRING | test(REGEX)
STRING | test(REGEX; FLAGS)
STRING | test([REGEX, FLAGS])
这个过滤器会输出一个_布尔_结果。
"Hello World!" | test("W") # => true
"Goodbye Mars" | test("W") # => false
当你需要提取真正匹配该模式的子字符串时,使用match过滤器。
STRING | match(REGEX)
STRING | match(REGEX; FLAGS)
STRING | match([REGEX, FLAGS])
这个过滤器会输出:
这个例子使用反向引用语法\1,查找两个相同的连续元音。
"Hello World!" | match("([aeiou])\\1")
# => empty
"Goodbye Mars" | match("([aeiou])\\1")
# => {
# "offset": 1,
# "length": 2,
# "string": "oo",
# "captures": [
# {
# "offset": 1,
# "length": 1,
# "string": "o",
# "name": null
# }
# ]
# }
match过滤器会为_每个_匹配返回一个对象。这个例子演示了"g"标志如何用来查找所有元音。
"Goodbye Mars" | match("[aeiou]"; "g")
# => { "offset": 1, "length": 1, "string": "o", "captures": [] }
# { "offset": 2, "length": 1, "string": "o", "captures": [] }
# { "offset": 6, "length": 1, "string": "e", "captures": [] }
# { "offset": 9, "length": 1, "string": "a", "captures": [] }
与match过滤器类似,capture过滤器在有匹配时会返回一个对象。
STRING | capture(REGEX)
STRING | capture(REGEX; FLAGS)
STRING | capture([REGEX, FLAGS])
返回的对象是命名捕获的映射。
"JIRAISSUE-1234" | capture("(?<project>\\w+)-(?<issue_num>\\d+)")
# => {
# "project": "JIRAISSUE",
# "issue_num": "1234"
# }
scan过滤器类似于带"g"标志的match。
STRING | scan(REGEX)
STRING | scan(REGEX; FLAGS)
# note, there is no scan([REGEX, FLAGS]) version, unlike other filters
scan会输出一个_子字符串流_。
"Goodbye Mars" | scan("[aeiou]")
# => "o"
# "o"
# "e"
# "a"
使用[...]数组构造器来捕获这些子字符串。
"Goodbye Mars" | [ scan("[aeiou]") ]
# => ["o", "o", "e", "a"]
如果你知道字符串中想要保留的部分,就用match或scan。如果你知道想要丢弃的部分,就用split。
STRING | split(REGEX; FLAGS)
单参数的split过滤器会把它的实参当作固定字符串处理。
要在split中使用正则表达式,你必须提供第 2 个实参;使用空字符串也是可以的。
下面这个例子按任意空白字符拆分字符串。
"first second third fourth" | split("\\s+"; "")
# => ["first", "second", "third", "fourth"]
如果我们忘了标志参数,就会得到这样的结果。
"first second third fourth" | split("\\s+")
# => ["first second third fourth"]
只有一个结果:输入中并没有出现_固定字符串_\s+。
单参数的split无法处理_任意_空白字符。按空格拆分则会得到这样的结果。
"first second third fourth" | split(" ")
# => ["first", "", "", "second", "", "", "", "",
# "", "", "", "", "", "", "third", "fourth" ]
sub和gsub过滤器可以转换输入字符串,把输入中匹配到的部分替换为替换字符串。
只替换第一个匹配项,就用sub。
替换所有匹配项,就用gsub。
STRING | sub(REGEX; REPLACEMENT)
STRING | sub(REGEX; REPLACEMENT; FLAGS)
STRING | gsub(REGEX; REPLACEMENT)
STRING | gsub(REGEX; REPLACEMENT; FLAGS)
"Goodnight kittens. Goodnight mittens." | sub("night"; " morning")
# => "Good morning kittens. Goodnight mittens."
"Goodnight kittens. Goodnight mittens." | gsub("night"; " morning")
# => "Good morning kittens. Good morning mittens."
替换文本可以引用匹配到的子字符串;使用命名捕获和字符串插值。
"Some 3-letter acronyms: gnu, csv, png"
| gsub( "\\b(?<tla>[[:alpha:]]{3})\\b"; # find words 3 letters long
"\(.tla | ascii_upcase)" ) # upper-case the match
# => "Some 3-letter acronyms: GNU, CSV, PNG"
在上面所有的过滤器中,FLAGS 是一个由零个或多个受支持的标志组成的字符串。
g - 全局搜索(查找所有匹配项,而不只是第一个)i - 不区分大小写的搜索m - 多行模式('.' 会匹配换行符)n - 忽略空匹配p - 同时启用 s 和 m 模式s - 单行模式('^' -> '\A','$' -> '\Z')l - 查找尽可能长的匹配x - 扩展正则表达式格式(忽略空白和注释)例如
"JIRAISSUE-1234" | capture("(?<project>\\w+)-(?<issue_num>\\d+)")
# or with Extended formatting
"JIRAISSUE-1234" | capture("
(?<project> \\w+ ) # the Jira project
- # followed by a hyphen
(?<issue_num> \\d+ ) # followed by digits
"; "x")