15 Vim 심화- 찾아 바꾸기 (substitute)
- vim
- 2021. 12. 20.
이번 시간에는 Vim 에서 어떻게 '찾아 바꾸기'를 하는지 알아보도록 하자. 'Vim 에서 찾기' 기능은 앞선 '기본조작'편에서 이미 알아보았다. '찾기'는 [The Vim Way] 09 Vim 기본조작 - Vim 문자열 찾기 편에서 알아볼 수 있다.
Vim 찾아 바꾸기
요즘 IDE 또는 텍스트에디터에서는 '찾아 바꾸기'는 매우 기본적으로 탑재되어있는 기능 중에 하나이다. 특히 jetbrains 계열의 IDE 에서는 정규표현식, 대소문자 구분 여부, 단일 단어 여부에 따라 찾기 및 찾아바꾸기 기능을 지원한다. 즉, vim 에서 제공하는 찾아바꾸기를 굳이 이용할 이유가 없는 것도 사실이지만, original vim 을 이용해야하는 상황이거나, 범위를 지정하여 찾아바꾸기를 진행해야 하는 경우 등의 상황이라면 이번 포스팅에서 다룬 내용이 유용할 수 있을 것이다. 이 포스팅은 '과도하게(?) 자세히' 본 기능에 대해서 소개한다. 바쁜 당신을 위해 바로 아래 '빠르게 살펴보기'를 준비했다.
Vim 찾아바꾸기 Quick View
Normal 모드에서 다음을 입력하면 문서 전체에 대하여 나타나는 모든 'something' 을 'sth' 로 변경한다.
(대소문자 관계 없이 - case insenstive - 하게 변경하고자 한다면, g
뒤에 i
를 덧붙여준다.)
:%s/something/sth/g
아래는 이 명령의 동작 원리를 상세하게 풀어 설명한다.
Vim 찾아 바꾸기 기본 (현재 라인)
vim 에서 찾아 바꾸기는 '커맨드모드'에서 이루어진다. 커맨드모드에서 다음 명령어를 입력하여 현재 커서가 위치하는 라인에 대하여 '찾아 바꾸기'를 수행할 수 있다.
:
로 커맨드모드 진입s/{찾을 대상 regex}/{바꿀 문자열}
입력
:s
는 커맨드모드에서 '찾아바꾸기'를 의미하는 명령이다. (substitute 의 첫 글자)
첫 번째 슬래시(/) 이후에는 '찾을 문자열(정규표현식)'을 입력한다. (보통 pattern 이라고 표기한다.)
두 번째 슬래시(/) 이후에는 '바꿀 문자열'을 입력한다.
가령 다음과 같이 입력하면 'show' 라는 문자열을 'give' 라는 문자열로 변경하는 명령어를 의미하게 된다. 단, 커서가 위치한 라인만 해당 사항이 적용된다는 것과, '찾을 대상'으로 지정한 첫 번째 대상만 (정규식의 특징) 변경된다는 점에 유의한다.
:s/show/give
'찾을 대상'은 사실 정규표현식을 지정하는 곳이다. 다음처럼 입력하면 w 로 시작하는 단어(regex: [w]\w+
)'를 모두 'path' 로 변경해준다.
:s/[w]\w+/path
Vim 매칭되는 모든 대상 변경하기
위에서 살펴보았듯이 정규표현식(찾을 문자열)과 매칭되는 첫 번째 대상만이 변경되는 것을 확인할 수 있다. 문장 'show us the way to wake' 에서 정규표현식 [w]\w+
에 매칭되는 문자열은 'way' 와 'wake' 인데, 이 둘을 모두 변경하고 싶다면 어떻게 해야할까?
(비추천)Vim 이전 커맨드모드 명령어 재실행
첫 번째 방법은 이전에 수행한 커맨드모드 명령을 다시 반복하면 된다. Vim 이전 커맨드모드 명령 반복은 @:
이다. 이전 시간([The Vim Way] 13 Vim 기본조작 - 기본조작편 요약 (ft. viEmu Cheat Sheet), Vim 학습 커리큘럼)에 살펴보았듯 @{register} 명령어는 register 에 저장된 매크로 실행인데, :
레지스터는 이전 커맨드모드 명령이 저장되는 곳이다. 따라서, @:
는 이전 커맨드모드 명령어를 재실행한다. 다음 스크린샷에서처럼 '이전 커맨드모드 명령어 실행 - @:
' 기능을 이용하여 순차적으로 way 를 path 로 변경해보자.
💥 안타깝게도 VSCode Vim 에서는 escaping 문자가 들어간 커맨드모드 명령을 다시 반복하는 데 문제가 있어보인다. (2021년 12월 기준) 사실 커맨드모드 명령을 반복하는 것을 소개하기 위해 이번 장을 '굳이' 추가했다.
사실 '찾아 바꾸기'를 이렇게 사용하는 사용자는 아마 지구상에 존재하지 않을지도 모른다.
정규표현식에 global 지정자 추가
다음과 같이 정규식의 마지막에 'global' 을 의미하는 g 를 추가하여 '찾을 문자열'과 매칭되는 모든 대상에 대해 바꾸기를 수행할 수 있다. 사실, 라인마다 매칭되는 첫번째 대상만을 변경하는 경우는 거의 없다. 따라서 /g
는 거의 필수로 들어가게 된다.
:s/way/path/g
Vim 대소문자 관계없이 매칭되는 모든 대상 변경하기
g 뒤에 i 를 추가해준다. 이는 case-insensitive 를 의미한다. 다음 명령의 의미는 '대소문자 관계없이 모든 way 를 path 로 변경하라' 라는 의미가 된다.
:s/way/path/gi
Vim 범위 지정하여 찾아 바꾸기
- Visual Mode 로 범위를 지정
:
로 커맨드모드 진입 -> 자동으로 지정한 범위(:'<,'>
표시)에 대해 커맨드모드 진입s/{찾을 대상 regex}/{바꿀 문자열}
입력
커맨드모드 명령어는 지정한 범위에 대해서만 적용되도록 할 수 있다. Visual Mode 로 범위를 지정한 뒤 :
을 입력하면 상태표시줄에 이전과는 조금 다른 표시가 보인다. '<,'>
표시는 지정된 블록의 시작('<
)과 끝('>
)을 의미한다. 따라서 해당 명령이 지정된 범위에 대해서만 작동할 것을 암시한다.
Vim 문서 전체를 대상으로 찾아 바꾸기
ggVG
:s/{찾을 대상 정규표현식}/{바꿀 문자열}/g
ggVG
는 3가지 명령의 조합이다. gg
는 문서의 맨 처음으로, V
(= shift
+ v
) 는 Visual Line Mode 진입, G
(= shift
+ g
) 는 문서의 맨 끝으로 이동하는 명령이다. 즉, 문서 전체를 선택하여 Visual Mode 로 진입한다. 이후 :
를 입력하면, 지정된 범위를 표현하는 '<,'>
표시와 함께 커맨드모드가 시작된다. 이 때 s/{찾을 대상 정규표현식}/{바꿀 문자열}/g
를 입력하여 바꾸기 명령을 수행할 수 있다. 마찬가지로 명령어의 오른족 끝에 /g
를 붙여 매칭되는 모든 토큰을 변경하겠다는 것을 명시해주었다.
하지만 더 나은 방법이 있다.
Vim 특정 라인을 지정하여 찾아 바꾸기
vim command 모드 명령어는 :
이후에 범위를 지정할 수 있다. 앞선 예제에서 '<,'>
의 의미가 각각 블록 범위의 첫 행과 끝 행을 의미했던것과 동일한 원리로 라인 번호를 지정할 수 있다. 아래와 같이 입력하면 3번부터 5번 라인에 대해 나타나는 모든(/g
) 'something' 을 'sth' 로 변경할 수 있다.
:3,5s/something/sth/g
또한, 특수한 라인 번호를 지정할 수도 있다. 다음은 vim 커맨드모드 명령을 수행할 대상 라인 번호를 지정할 수 있는 다양한 방법이다.
0
- 첫 라인(1번 라인) 위의 가상의 라인, 사실상 1과 동일하다$
- 마지막 라인, 마지막 라인 넘버는 보통 가변적이므로 이렇게 지정한다.'<
- 블록 지정범위의 시작 라인'>
- 블록 지정범위의 끝 라인.
- 현재 커서가 위치한 라인'{mark}
- 해당 mark 가 포함되어있는 라인
💡 이번 연재를 통틀어 mark 를 다루지 않았습니다. mark 는 커서의 위치를 특정 문자에 매핑하여 기억할 수 있는 기능입니다. Vim Normal mode 에서m
명령 이후 기억할 문자(A~Z, a~z, 0~9)를 입력하면 해당 문자에 현재 위치가 기억됩니다. (:marks
로 기억된 마크들을 모두 확인할 수 있습니다.)
마크된 위치로`
{mark문자}
(=백틱
+마크기호
)로 이동할 수 있으며 해당 라인으로는`
{mark문자}
로 이동할 수 있습니다. 가령 Vim Normal Mode 에서mk
를 입력하면 현재 커서의 위치에 k 라는 마크를 지정하는 것입니다. 이후'k
(=따옴표
+마크기호
) 로 마크된 위치의 라인으로 이동할 수 있으며,`k
(=백틱
+마크기호
) 로 마크된 정확한 위치로 이동할 수 있습니다.'k
가 의미하는 것은k
마크가 위치한 라인'이 되므로, 굳이 마크를 활용해 라인을 지정하는 것은 커맨드모드에서 범위를'k
를 활용하여 지정하기 위해서입니다.
기회가 된다면, 추후 Vim Marking 에 관하여 포스팅을 하도록 하겠습니다.
가령 다음과 같이 써주면 문서의 전체(0
라인부터 $
라인까지)에 대해서 'something' 을 'sth' 로 변경하는 커맨드모드 명령이 된다.
:0,$s/something/sth/g
%
- 커맨드모드 문서 전체 지정자
문서 전체에 대해서라면 범위(시작과 끝)를 지정하는 대신 문서 전체를 지정하는 방법도 있다. 바로 %
이다. 다음처럼 사용하면 문서 전체에 대해서 'something'을 'sth' 로 변경한다. 즉, 1,$
범위의 축약형이 %
이다.
:%s/something/sth/g
Vim Substitution Structure
사실 vim substitute 명령은 다음과 같은 구조로 이루어져있다.
:[range]s/[pattern]/[substitution]/[flags]
[range]
range 는 범위를 의미하며 위에서 살펴보았던 것처럼 , 를 활용하여 특정 라인부터 특정 라인까지를 범위로 지정하거나, 문서 전체(%
)를 지정할 수 있었다.
[pattern]
pattern 은 정규표현식을 의미한다. 문자열을 그대로 입력할 수도 있지만, 정규표현식이므로 몇몇 문자는 정규표현식의 operator 임에 유의한다.
[substitution]
대체할 문자열이다. patten 에 정규식의 그룹을 활용했다면, 여기서 $1, $2 등을 활용해 그룹을 명시할 수 있다.
가령, <code>Hello! World!</code>
를 <c>Hello! World!</c>
와 같이 바꾸고 싶다면 다음과 같은 명령어로 가능하다.
:s/<code>(.*)<\/code>/<c>$1<\/c>/g
[flags]
정규표현식의 flags 와 거의 동일하다. 다양한 flags 들이 있지만, g
는 global(모든 대상에 대하여)을 의미하고, i
는 case-insensitive 를 의미한다.
Vim 찾아바꾸기 - 찾기&변경 응용
커맨드모드 명령 substitute 를 활용하여 '찾아 바꾸기'를 수행할 수도 있지만, 모든 변경을 수행하지 않고, 변경할 내용을 순차적으로 확인하면서 변경하는 방법도 존재한다.
/{pattern}
으로 찾을 문자열을 찾는다.ciw
명령어를 입력한 뒤 해당 단어를 수정한다.n
으로 다음 찾을 문자열로 이동한다.- (변경할지 여부를 결정한 뒤) 변경하려면
.
으로 해당 문자를 변경(이전 명령 - 3.에서 수행한 - 반복)한다. - 원하는만큼 3. ~ 4. 를 반복한다.
다음 문장에서 특정 will 만 William 으로 변경하는 예제를 스크린샷으로 보도록 하자.
이상으로 Vim 에서 찾아 바꾸기(substitute) 기능을 어떻게 활용할 수 있는지 다양한 방법들과 함께, 그 구조를 살펴보았다. 사실 요즘 IDE 들의 찾아바꾸기 기능은 거의 기초적인 기능이기에 큰 효용이 없을지도 모르는 기능이지만, 만약을 대비해 익혀두는 것 또한 나쁘지 않을 것 같다.
다음 포스팅에서는 프로그래머에게 특별히 유용한 surround 기능을 소개한다. 이 기능으로 괄호와 관련된 다양한 동작들을 수행할 수 있다.
'vim' 카테고리의 다른 글
16 Vim 심화 - Surround (1) | 2021.12.20 |
---|---|
14 Vim 심화 - Macro (매크로) (2) | 2021.12.18 |
13 Vim 기본 - 기본편 요약, 치트시트, 커리큘럼 (5) | 2021.12.06 |
12 Vim 기본 - undo(되돌리기), redo(재실행) (0) | 2021.12.05 |
11 Vim 기본 - 명령 n번 반복하기 (0) | 2021.12.05 |