콘텐츠로 건너뛰기
Home » vim에서 패턴에 매칭되는 영역을 추출하기

vim에서 패턴에 매칭되는 영역을 추출하기

지난 글에서 notepad++에서 특정 패턴에 매칭되는 영역만 추출하는 방법에 대해 소개하였는데요, 이번에는 vim에서 같은 기능을 어떻게 구현할 수 있는지에 대해서 알아보겠습니다.

:s/{pattern}/\=

버퍼 내의 텍스트를 치환하는 :s 명령에서, 패턴 다음에 오는 치환할 문자열 부분이 \= 으로 시작하는 경우에는 이 영역이 표현식(expression)으로서 평가됩니다. 만약 표현식을 평가한 결과가 리스트 타입인 경우에는 최종 결과는 개행 문자로 결합된 형태가 됩니다. 이 표현식 내부에서는 submatch()함수가 매치된 텍스트를 얻는데 사용될 수 있습니다. 매치된 전체 영역은 submatch(0)으로 표현하며, 만약 매치 패턴에 그룹을 지정했다면, 괄호 안에는 그룹의 번호가 들어갈 수 있습니다. 다음 명령에서는 setreg() 함수를 사용해서 치환할 부분을 평가하면서 검색된 패턴을 레지스터 a에 라인으로 추가합니다.

:let @a='' | %s/\v\d{8}-\d{7}/\=setreg('A', submatch(0), 'l')/gn | new | put! a

:s 명령에서 g 플래그는 한 라인에 하나 이상 매치가 있으면 모두 변경을 적용하는 것입니다. n 플래그는 매치에 대해서 치환을 적용하지 않고 스킵하는 플래그입니다. 또한 setreg() 함수에서는 레지스터에 설정한 값을 그대로 리턴하기 때문에, 결과적으로 이 명령을 실행하더라도 현재 버퍼에서 변경되는 내용은 없으며, 레지스터 a의 값에 매치될 내용들이 추가됩니다.

setreg() 함수

주어진 값을 지정한 레지스터에 저장하며, 그 값을 그대로 리턴합니다.

setreg({regname}, {value}[, {options}])
  • 'c' / 'v' : characterwise 모드
  • 'l' / 'V' : linewise 모드
  • 'b' or '<CTRL_V>' : blockwise-visual 모드

이 때 옵션에 'a'가 포함되어 있거나, 레지스터 이름이 대문자로 주어진다면 현재 레지스터의 내용을 변경하지 않고 추가하게 됩니다.

submatch() 함수

이 함수는 :s 명령이나, substitute() 함수 내부에서만 사용할 수 있는 명령으로, 패턴에 매칭된 부분에서 다시 하위 매치 그룹을 찾는 함수입니다. submatch(0) 과 같이 쓰면 패턴 전체에 일치하는 부분일 리턴하며, 1, 2..와 같은 번호를 사용하면 해당 번호의 그룹에 매치하는 영역을 리턴합니다.

: let @a='' |                       " 레지스터 a의 내용을 초기화
    %s/\v\d{8}-\d{7}/               " 00000000-0000000 의 형식으로 된 부분을 찾음
    \=setreg('A', submatch(0), 'l)  " 찾은 부분을  레지스터a에 추가 (레지스터 이름을 대문자로 씀)
    /gn |                           " 전체에 대해 실행하며, 매치 부분을 변경하지 않고 skip함
    new |                           " 새 버퍼를 열기
    put! a                          " 레지스터 a의 내용을 현재버퍼(새 버퍼)에 붙여넣음

사용자 정의 명령

매번 긴 명령어를 입력하기가 번거로우니, 마지막으로 검색한 패턴을 사용하여 매치되는 부분을 새 버퍼에 출력하도록 하는 함수를 작성하고, 이를 사용자 정의 명령으로 호출할 수 있도록 합니다. :substitute 명령을 실행할 때, 패턴 부분을 비워두면 마지막으로 사용한 검색 패턴을 재사용하게 됩니다.

vim9script

command -nargs=0 ExtractSearch ExtractSearchPattern()

def ExtractSearch()
  if @/ == ''
    return
  endif
  var temp = @a
  @a = ''
  silent :%s//\=setreg('A', submatch(0), 'al')/gn
  new
  put! a
  normal! gg
  nohls
  @a = temp
enddef

참고로 이렇게 추출한 값 중에서 중복을 제거하려면 :sort u 를 실행하면 됩니다. :sort u 는 정렬한 후 연속되는 라인이 중복된 라인이라면 자동으로 삭제합니다. 만약 라인의 순서를 변경하지 않으면서 중복된 라인을 제거하려면 별도의 외부 프로그램이나 vim script로 작성한 커스텀 함수를 사용하는 등 다른 방법을 동원할 필요가 있습니다.

관련글

댓글 남기기