正規表示式(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中使用正規表示式,你必須提供第二個引數;使用空字串也可以。
以下範例將字串依任意空白分割。
"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")