트랙
/
jq
jq
/
학습 과정
/
정규 표현식
정규

정규 표현식 에서 jq

1개의 연습 문제

정규 표현식 소개

정규 표현식(정규식)은 텍스트에서 검색 패턴을 지정하는 문자들의 나열이에요.

정규 표현식 문법을 배우는 것은 이 주제의 범위를 벗어나요. 여기서는 jq가 정규 표현식을 활용하기 위해 제공하는 필터에 초점을 맞출게요.

정규 표현식의 종류

도구마다 서로 다른 버전의 정규 표현식을 구현해요. jq는 Perl v5.8 정규 표현식과 대부분 호환되는 Oniguruma 정규 표현식 라이브러리를 사용해요.

jq가 사용하는 구체적인 문법은 Oniguruma GitHub 저장소에서 확인할 수 있어요.

Caution

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)
Caution

인자를 하나만 받는 split 필터는 인자를 고정 문자열로 취급해요.

split에서 정규 표현식을 사용하려면 두 번째 인자를 반드시 넘겨야 해요. 빈 문자열을 넘겨도 괜찮아요.

임의의 공백을 기준으로 문자열을 나누는 예제예요.

"first   second           third fourth" | split("\\s+"; "")
# => ["first", "second", "third", "fourth"]
Note

플래그 인자를 빠뜨리면 이렇게 돼요.

"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는 지원되는 플래그 중 0개 이상으로 이루어진 문자열이에요.

  • 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")
GitHub에서 편집 링크가 새 창이나 탭에서 열려요

정규 표현식 배우기

연습이 잠겨 있어요

정규 표현식 개념을 연습하려면 연습 문제 1개를 더 잠금 해제해요