검색 및 정규표현식

이번 회에서는 vim 내에서 특정 표현을 검색하고 치환하는 방법을 배워보도록 하겠다. 좀더 복잡한 패턴의 검색을 위해서는 정규표현식이라는 것을 사용해야만 한다. 비단 vim 뿐만 아니라 많은 유틸리티에서도 사용하는 정규표현식을 어느 정도 익히게 되면 어렵게만 느껴졌던 많은 명령어들이 그만큼 친숙하게 다가올 것이다.

1. 검색

이번 장에서는 vim을 통해 검색하기 위해 다음과 같은 기본적인 내용에 다루기로 한다.

∙순방향, 역방향 검색

∙검색 옵션

1.1 순방향, 역방향 검색

일반적인 검색이란 문서의 시작 혹은 현재 위치에서 문서의 끝방향으로 검색을 해나가게 되는데 이를 순방향 검색이라 한다. 반대로 현재의 위치에서 문서의 처음 방향으로 검색을 해나갈 수도 있는데 이를 역방향 검색이라 한다. 이 두 가지 방식의 검색은 단지 종이 한 장 차이만 날뿐 복잡한 차이는 없다.

vim는 '/' 명령으로 순방향 검색을 실시한다. "Normal Mode"에서 '/'를 쳐주게 되면 다음 그림처럼 커서가 좌측하단으로 이동하면서 밑줄에 '/' 가 적히게 된다. 이 상태에서 검색하고 싶은 문자열을 적어주면 된다. 반대로 역방향 검색은 '/' 대신 '?' 로 검색 명령을 내리면 된다.

/ 나 ? 로 검색 명령을 내린 뒤 <ENTER>를 치게되면 커서가 처음 발견한 위치에 놓이게 된다. 같은 검색을 계속하기 위해서는 "Normal Mode" 상태 그대로에서 'n'을 누르면 된다. 이 때 검색 방향으로 다음 발견한 위치에 커서가 놓이게 된다.

다음 예에서 ‘/it’ 으로 검색 한 후 'n'을 누르게 되면 14, 16, 17, 22 번째 줄 순서대로 커서가 이동해가게 된다.

13 A love like ours is love that's hard to find

14 How could we let it slip away?

15

16 We've come too far to leave it all behind

17 How could we end it all this way?

18 When tomorrow comes and we both regret

19 The things we said today

20

21 A love like ours is love that's hard to find

22 How could we let it slip away?

위의 예에서도 보듯 'n'은 검색 방향으로 검색 결과를 추적해 간다. 만약 검색방향의 반대 방향으로 추적을 하고 싶으면 'N'을 누르면 된다.

한가지 주의 할 것은 vim을 통해 검색할 때 ‘^$*[]’ 등과 같은 특수 문자는 뒤에서 배울 정규표현식을 위해 특별한 용도로 사용된다. 그렇기 때문에 '$' 문자 자체를 검사하고 싶다면

$ 앞에 \를 추가하여 \$ 와 같이 검색해야 한다.

1

2 $ dollar

3 Good Morning!

4

위의 예에서 '$' 라는 문자를 검색하기 위해 /$ 로 명령을 내리면 아마 검색 결과가 줄의 끝을 가르킬 것이다. 이에 대한 자세한 이유는 뒤에 설명하기로 하고 제대로 한다면 /\$ 로 검색하기 바란다.

여러 가지 검색어로 검색을 한 경우 vim 는 history 기능으로 전에 검색했던 검색어를 쉽게 재활용 할 수 있다.

지금까지 다음과 같이 세 검색어로 검색을 한 경우

/aaa

/bbb

/cccc

/를 누른 상태에서 화살표 Up, Down 키를 누르면 전에 검색했던 검색어로 찾아 들어가게 된다. 원하는 검색어를 찾을 경우 <ENTER> 만 쳐주면 다시 검색을 실시 할 수 있다.

1.2 검색 옵션

이번 절에서는 검색 결과나 방법에 영향을 줄 수 있는 몇 가지 옵션에 대해 알아보도록 하겠다. 특별한 옵션이 없다면 vim 은 검색할 때 대소문자를 구별한다. 그래서 /abc 와 /ABC 는 전혀 다른 검색 결과를 보여주게 된다. 대소문자를 구분하지 않고 검색하고 싶다면 "Command Mode"에서 다음과 같은 명령을 내려주면 된다.

:set ignorecase 혹은 :set ic

다시 대소문자를 구분하고 싶으면

:set noignorecase

명령을 내려주면 된다.

일단 vim에서 검색을 하게 되면 검색어에 해당되는 결과는 바탕이 강조되어 보이게 된다. 이 결과는 다른 검색을 할 때까지 그대로 표시가 되는데 가끔 편집할 때 눈이 피로해 보이는 경우가 있다. 만약 강조(highlight)되는 것을 없애고 싶다면 다음과 같은 명령을 내려주면 된다.

:set nohlsearch

다시 강조하게 하고 싶으면

:set hlsearch

명령을 내려주면 된다.

vim 이 디폴트로 세팅되어 있다면 검색어를 완벽히 입력하고 <ENTER>를 쳐야만 비로소 검색을 시작하게 된다. 이와 반대로 ‘find' 라는 단어를 찾을 때 f, i, n, d 한글자 한글자 입력할 때마다 그때 그때의 결과를 바로 볼 수 있는 검색을 incremental searching 이라고 한다. 이러한 검색을 사용하면 정확히 찾고자 하는 단어가 떠오르지 않을 때 조금씩 단어를 완성해가면서 검색을 할 수 있다. 검색 방법을 incremental searching 으로 바꾸려면

:set incsearch

이란 명령을 입력하면 된다. 반대로 해제는 다른 옵션과 마찬가지로 앞에 no를 앞에 붙여

:set noincsearch

명령을 내려주면 된다.

2. 정규 표현식(regular expression)

정규표현식이란 주로 문자열 내에서 검색이나 조작에 사용되는 표현식으로써 특정한 패턴을 가지고 그 패턴에 부합되는 문자열을 찾아내 원하는 일을 수행 할 수가 있다. 정규표현식은 비단 vim 뿐만 아니라 sed, grep, 과 같은 유틸리티부터 시작해서 vim, emacs 와 같은 편집기 그리고 perl, php 와 같은 프로그래밍 언어에까지 문자열을 다루는 프로그램에는 거의 모두 사용되고 있다. 하지만 정규표현식이 쓰이는 모든 프로그램에서 동일한 문법으로 정규표현식을 규정하고 있지는 않는다. 그렇기 때문에 vim에서 사용하는 정규표현식을 완벽히 익혔다 하더라도 다른 프로그램에서 정규표현식을 사용할 때는 표현이 적절한 지 미리 알아 보아야 한다. 하지만 그렇다고 너무 걱정할 필요는 없다. 문법이 다르다고는 하지만 기본적인 내용은 모두 같고 약간의 형식만 다르기 때문이다. 이번 절에서는 정규표현식의 기본적인 문법을 검색으로 실습하며 익혀 보기로 하겠다.

2.1 문자 패턴

[abc] : a, b, c 문자 중 한 문자를 가리킨다.

만약 b[abc]ll 라고 검색을 하면 ball, bbll, bcll 이 세 개의 문자열 중 하나가 검색 될 것이다. [] 안에는 알파벳이나 숫자가 올 수 있고 이 안에 나열되어 있는 문자 중 한 문자를 가리키게 되는 것이다. 즉 [abc] 는 검색에서 한 문자처럼 취급을 받을 것이다. 만약 [] 안에 특수 문자를 넣고 싶으면 앞에 꼭 \를 넣어줘야 원하는 패턴을 만들 수 있다.

[a-z], [0-9], [A-Z0-9] : [] 안에는 문자의 범위가 들어 갈 수도 있다.

[a-z] 는 결코 a, -, z 라는 문자 중 한 문자를 가리키지 않는다. 대신 알파벳의 모든 소문자 a, b, c .... x, y, z를 가리킨다. - 가 [] 안에 들어가게 되면 문자의 범위를 나타내는 특수한 역할을 하게 되는 것이다. 비슷하게 [0-9] 는 숫자 [A-Z0-9] 는 알파벳 대문자와 숫자를 나타낸다.

. : 정규표현식에서 . 은 new line 문자(엔터)를 제외한 모든 문자를 가리킨다. 즉 알파벳 혹은 공백과 같은 특수문자, 숫자 모두 . 에 속하게 된다.

[^a-z] : ^ 가 [] 안에 들어오면 안에 나열되어 있는 문자를 제외한 다른 모든 문자를 나타낸다. 이와 같이 하면 알파벳 소문자를 제외한 모든 문자, 즉 대문자, 숫자, 특수문자 모두가 이에 해당된다.

특수문자 : 특수문자는 항상 \ 와 같이 쓰여야 문자 그대로의 의미를 나타낸다. 공백(Space)는 '\ ' 로 Space 앞에 \를 붙여주면 된다.

그럼 지금까지 익힌 문자 패턴을 이용하여 몇가지 예를 들어 보기로 하겠다.

첫문자는 무조건 대문자로 시작하고 곧바로 숫자가 2자리 오는 패턴을 정규표현식으로 만들면 어떻게 될까? 대문자는 [A-Z], 숫자는 [0-9], 그리고 숫자가 2번 오기 때문에

[A-Z][0-9][0-9]  이렇게 하면 표현식이 완성된다.

2.2 위치

^문자열 : '^' 다음에 문자열이 오면 해당 문자열이 줄의 처음에 오는 경우이다.

밑의 문장에서 /^The 로 검색을 하게 되면 줄의 맨 처음에 위치한 The 만 표현식에 해당된다. [ ] 내에 있던 ^ 과 혼동하지 않기를 바란다.

The things we said today, The things we said today

두 번째 The 는 문장의 처음에 오지 않았기 때문에 표현식과 맞지 않는 표현이다.

문자열$ : 문자열 다음에 $를 붙이게 되면 해당 문자열이 줄의 끝에 오는 경우를 가리킨다.

위의 예에서 /today$ 로 검색하면

The things we said today, The things we said today

뒤에 있는 today 만 검색된다. 가운데 있는 today 는 줄의 끝에 있지 않기 때문이다.

2.3 반복

반복을 지정하는 표현식은 문자 뒤에 적어 앞에 있는 문자가 몇 개까지 반복되는 지를 지정한다.

* :  0 번 이상

\+ : 1 번 이상 (vim only)

\? : 0번 혹은 1번 (vim only)

\+ 와 \? 는 전통적인 vi 에서는 제공되지 않고 vim 에서만 사용할 수 있다. 물론 리눅스에 설치되어 있는 vi는 vim 이기 때문에 사용 가능하다. 그럼 차근차근 예를 들어 설명해 보면,

ab* 는 b라는 문자가 없을 수도 있고 한번 이상 올 수도 있다. 그렇기 때문에 a, ab, abb, abbb, 모두 이 표현식을 만족한다.

ab\+ 는 ab* 와 달리 반드시 b 라는 문자가 한번 이상 와야 한다. 때문에 a 는 이 표현식을 만족시키지 못한다. ab, abb, abbb처럼 반드시 a 뒤에 b가 한번 이상은 존재해야만 한다.

ab\? 는 a 뒤에 b가 올 수도 오지 않을 수 도 있다. 즉 a, ab 둘다 이 표현식을 만족한다.

그럼 abc 와 같은 문자열이 반복되는 정규표현식은 어떻게 만들 수 있을까? 바로 문자들을

\( 와 \) 안에 묶어 두면 된다.

\(abc\)* 는 abc 문자열이 0번 이상 반복되는 표현식이고

\(abc\)\+ 는 abc 문자열이 1번 이상 반복되는 표현식이다.

※ 만약 여러분이 /\(abc\)* 와 같은 검색을 명령을 내리면 vi 는 아마도 모든 문자가 검색결과로 지정 될 것이다. 왜냐하면 * 특성 자체가 0번도 인정하기 때문에 굳이 abc 라는 문자열이 없어도 표현식을 만족하기 때문이다.

짧은 단어를 검색하고자 할 때 간혹 그 단어가 포함된 다른 단어까지 같이 검색 되어지곤 할 것이다. 일 예로 'be' 란 단어를 검색하다보면 behind 와 같이 검색어가 포함된 단어도 같이 검색이 된다. 만약 be 앞뒤로 공백 문자가 있는 단어 자체를 검색하고 싶을때, 지금까지 배운 방법을 사용하면 '\ be\ '처럼 할 수 도 있다. 하지만 <TAB> 이나 <ENTER>까지 모두 고려하려다 보면 표현식이 약간 지저분해 질 수 있다. 이때 깔끔히 사용할 수 있는 정규표현식이 \<\> 이다.

/\<be\> 로 검색을 하면 완벽한 단어인 be (앞뒤로 공백문자가 있는) 가 검색 된다.

지금까지 배운 정규표현식을 응용하여 검색을 해보도록 하자.

반드시 줄의 시작은 알파벳 하나 이상으로 시작하고 중간에 하나 이상의 공백 문자 다음에 2개 이상의 숫자가 오는 패턴은 어떻게 정규표현식으로 나타낼 수 있을까?

먼저 하나 이상의 알파벳으로 줄이 시작하기 때문에

^[A-Za-z]\+

와 같은 표현식을 쓸 수 있다. 대소문자를 따로 언급하지 않았기 때문에 대문자와 소문자 모두 속해 있어야 한다.

중간에 하나 이상의 공백 문자가 오는 것까지 첨가하면

^[A-Za-z]\+\ \+

로 \ \+ 가 추가되었다.

끝으로 2개 이상의 숫자가 오기 때문에

^[A-Za-z]\+\ \+[0-9][0-9]\+  와 같이 쓸 수 있다.

3. 바꾸기

검색과 함께 빼놓을 수 없는 것은 바꾸기 일 것이다. 대부분 검색을 하게되면 검색으로 끝나지 않고 다른 단어로 바꾸는 작업이 같이 이루어진다.

3.1 바꾸기 문법

검색한 패턴을 다른 단어로 바꾸기 위해서는 먼저 범위를 지정하고 검색어와 변경할 문자열을 지정하면 된다. 이번 절에서는 기본적인 바꾸기 작업을 배워 보도록 하겠다.

바꾸기 위한 기본적인 문법은 다음과 같다.

:<시작줄 번호>,<끝 번호> s/검색어/바꿀단어/옵션

위의 문법으로 다음과 같은 예를 들 수 있다.

:1,10 s/The/the/g

해석해 보면 1번째 줄에서 10번째 줄 사이에 있는 "The" 라는 단어를 "the"로 바꾸라는 명령이다. 맨 끝의  g 라는 옵션은 모든 검색어에 대해 변경을 실시하게 해준다. 만약 g 옵션이 없을 경우 한 줄에 2 개 이상의 The 가 있을 경우 첫 번째 "The"  만 "the"로 변경을 하게 된다.

이외에 줄 번호를 가리키는 특별한 문자가 있다.

. : 현재 커서가 놓여 있는 줄의 번호

$ : 마지막 줄

:.,$ s/The/the/g

위의 명령은 현재에서 마지막 줄까지 모든 "The"를 "the" 로 바꾸게 한다.

바꾸기 문법중 검색어에는 2장에서 배운 정규표현식을 사용 할 수 있다. 만약 모든 줄에서 다른 단어에 포함되지 않은 "be" 라는 단어를 검색하여 대문자 "BE"로 변경하고 싶다면

:1,$ s/\<be\>/BE/g

처럼 명령을 내릴 수 있다.

만약 정규표현식에 익숙하지 않다면 완벽한 예제를 보며 차근차근 익히기 바란다.

3.2 정규표현식과 변수

2장의 정규표현식에서 \( \) 는 문자열을 하나의 단위로 보게 하는 역할을 하였다. 하지만 바꾸기에 사용 될 경우 변수 역할도 하게 된다.

abcd 100   이란 문장과 \([a-z]\+\)\ \([0-9]\+\) 이러한 정규표현식이 있다고 하자.

위의 문장과 정규표현식을 분석해 보면 앞의 abcd 는 뒤의 정규표현식 \([a-z]\+\) 에 해당하고 숫자 100 은 \([0-9]\+\) 에 해당하는 것을 알 수 있을 것이다. 그리고 가운데 공백은 정규표현식 '\ ' 에 해당한다.

정규표현식에서는 \( \)에 대해 하나의 단위로 묶으면서 동시에 \1, \2 와 같은 변수에 대응을 시킨다. 즉 위와 같은 경우 첫 번째  \( \) 는 \1에, 두 번째 \( \) 는 \2 라는 변수에 저장되는 것이다.

다음과 같은 문서가 있다고 보자.

height	150

weight	60

age	20

위와 같은 문서에서 문자와 숫자의 위치를 바꿔 다음과 같이 만들고 싶다.

150	height

60	weight

20	age

정규표현식의 변수와 vim의 바꾸기를 사용하여 앞 뒤 순서를 한번 바꾸어 보도록 하겠다.

:1,$ s/\([a-z]\+\)\(\ \+\)\([0-9]\+\)/\3\2\1/g

다소 복잡해 보이지만 차근차근 분석해 보도록 하겠다.

1,$ :  검색의 범위를 처음에서 끝 줄까지로 지정을 하였다.

s/.../.../g : 언제나 검색 및 바꾸기는 이러한 문법을 사용한다.

자 그럼 검색어 부분을 분석해보도록 하자.

\([a-z]\+\)\(\ \+\)\([0-9]\+\)

이 검색어를 \( \) 로 구분해 보면 다음 3개로 나뉘어 진다.

\([a-z]\+\)	→	\1	알파벳 문자 1개 이상

\(\ \+\)	→	\2	공백 문자 1개 이상

\([0-9]\+\)	→	\3	숫자 1개 이상

자 그럼 여기까지 검색어 부분을 분석해 보았다. 그럼 변경할 문자열을 분석해 보자.

\3\2\1

이것은 검색어 \1\2\3 의 순서만 바꾼 것이다.

자 그럼 해당 줄 별로 변수에 들어간 내용을 표로 그려보면

내용	\1	\2	\3	\3\2\1
height 150 weight 60 age 20	hegit weight age	" " " " " "	150 60 20	150 height 60 weight 20 age

끝으로 바꾸기 명령과 같이 쓰일 수 있는 옵션을 하나 더 알아보도록 하겠다.

지금까지는 바꾸기 명령은 구간내의 모든 검색 결과에 대해 바꾸기를 수행하였다. 물론 모든 검색 결과를 바꾸는 경우라면 문제가 없겠지만, 그 중에 변경하지 않아야 할 내용이 있다면 선택적으로 바꾸기를 행하고 싶을 수도 있을 것이다. 검색 결과에 대해 일일이 변경 여부를 물어보면 선택적으로 변경할 수 있는 옵션이 있다.

:1,$ s/.../..../gc

변경 전 확인

바꾸기 명령에 'c' 옵션을 추가하면 변경전 하나한 확인한 후 바꾸기를 실시하게 된다.

2회차에서는 편집에 꼭 필요한 검색 및 바꾸기를 해보았다. 정규표현식의 경우 어렵게 느껴졌을 수도 있겠지만, vim 뿐만 아니라 다른 프로그램에서도 활용될 수 있기 때문에 당장 이해가 되지 않더라도 꾸준히 연습할 가치가 있을 것이다.