2. 찾기, 치환

vi에서 찾기 명령과 치환 명령은 거의 예술에 가깝습니다. 거의 모든 문자열 을 찾고 다른 문자열로 대체할 수 있는 듯 합니다. 그 만큼 명령에 기능도 많고 사용 하기도 조금은 복잡합니다. 찾기와 치환은 ex 모드에서 행해집니다. 우선은 정규 표 현식을 알아두시면 찾고자 하는 패턴을 쉽게 찾는데 많은 도움이 됩니다.

2.1. 정규표현식

정규표현식은 vim의 경우 :help라고 입력하면 나오는 정규표현식 기호가 설 명이 잘 되어있습니다. 정규표현식은 각자 공부하시길 바랍니다. 여기서는 매우 간단 하게 기호 설명과 간단한 예만 들고 넘어가겠습니다. 자세히 하면 이것만 가지고도 한참을 설명하여야 하니까요... 아래의 표에 복잡한 기호들이 많이 나와있는데요. 우선 ., ^, \+, *, [], \< \>의 기호는 반드시 아셔야 합니다. 정말 기본적인 기호들이고 몇 개는 shell상이나 다른 곳에서도 사용하실 수 있는 것들이니까 알아두시면 도움이 되실 겁니다. 이 기 호들을 간략히 설명하면 .는 하나의 모든 문자를 나타냅니다. .라고 하면 이 기호에 어떠한 한 문자가 대치될 수 있는 것이지요. ^은 대괄호 안과 밖에서 의미가 달라지 는 데 대괄호 안에서는 not의 의미를 가지고, 밖에서는 위에서 명령모드에서 ^기호의 용도와 비슷하게 행의 처음을 나타냅니다. \+는 1회 이상 나타난다는 것을 의미합니 다. *도 비슷한데 이것은 0번 이상 즉 나타날 수도 있고 그렇지 않을 수도 있습니다. 마지막으로 []은 나타날 한 문자의 범위를 지정할 때 사용됩니다. 즉 [abc]는 a 또는 b 또는 c입니다. [a-z]는 a에서 z중에서 한 문자를 나타내겠지요. 그럼 이 기호들을 가지고 찾고자 하는 문자열의 패턴을 만들어 봅시다. The 나 the를 기호로 나타내면 [Tt]he 가 됩니다. 맞죠? []가 정해진 범위에서 한 문자를 나타낸다고 했으니까요. 그럼 행의 맨 처음에 나타나는 The나 the를 찾으려면 어떻게 해야 할까요? ^[Tt]he가 되겠죠. 이번엔 th로 시작하는 아무 문자를 만들어 봅시다. th로 시작하니까 th를 앞에 써주고 뒤에 아무 문자나 나오든지 안나오든지 상관이 없죠? th.* 이렇게 검색을 하면 the, this, that 등등이 검색이 되지만 although등도 검색이 됩니다. th로 시작하는 문자만 검색을 하려면 단어의 시작과 끝을 명시해주어 야겠죠. 그래서 필요한 것이 \< \>입니다. 이 사이에 위의 표현식을 넣어서 검색을 하면 the, this, that등과 같이 th로 시작하는 문자열을 검색하게 됩니다. *대신에 \+를 넣으면 어떻게 다를까요? th 의 경우 *라면 해당되겠지만 \+라면 해당되지 않습 니다. 이상이 간단한 정규 표현식에 대한 기호이고 이제 본격적으로 검색과 치환을 해 보도록 하죠.
(이 안은 원래 문자를 나타내는 식)
., (\.) 아무 문자나 한 문자
^, (^) 행의 처음에 시작				
$, ($) 행의 끝을 의미			
\<, (\<) 단어의 시작					
\>, (\>) 단어의 끝			
[a-z], (\[a-z]) 주어진 범위 내에서의 한 문자		
[^a-z], (\[^a-z]) 주어진 번위 밖에서의 한 문자		
\e, (\e) Esc							
\t, (\t) Tab					
\r, (\r) CR							
\b, (\b) BS						
*, (\*) 0 번 혹은 1번 이상 나타남
\+, (\+) 적어도 한 번 이상 나타남	
\l, (\l) 또는 			
POSIX character classes라는 것도 있는데 이것도 정규표현식 내에서 쓸 수 있습니다. 이러한 새로운 기호들은 모두 위에서 제가 설명한 것만으로도 만들 수 있 지만 식을 짧게 하기 위해서 미리 만들어 놓은 것이라고 생각하시면 됩니다. 예를 들 어 뒤에 확장 정규표현식에 \p를 정규표현식으로 하려면 [0-9a-zA-Z \t][0-9a-zA-Z \t]* 뭐 이런 식으로 표시해야겠죠(틀릴 수도 있습니다). 정규표현식에 자신 있는 분 은 모르겠지만 대부분은 많이 생각하여야 하고 저 처럼 혹시 빼먹은 문자가 생길지도 모르기 때문에 이런 식으로 또 다른 기호를 만들어 표현하는 것이죠.
[:alnum:]	알파벳, 숫자
[:alpha:]	알파벳
[:blank:]	공백문자 탭문자 포함
[:cntrl:]	control 문자
[:digit:]	숫자
[:graph:]	공백문자를 제외한
[:lower:]	소문자
[:upper:]	대문자
[:print:]	프린트가 가능한 문자 공백 포함
[:punct:]	구두점문자 ) ( , . " ' : ; - 등
[:space:]	공백문자
[:xdigit:]	16진수 숫자

2.2. 찾기

단순히 패턴만 찾는 거라면 명령모드에서 /pattern 또는 ?pattern 과 n, N키 를 이용해서 찾는 것이 편리합니다. ex모드에서의 패턴을 찾기 위해서는 :/pattern/ 형식을 사용합니다. ex모드에 이 찾기 명령이 있음으로 해서 다른 ex명령들과 조합이 가능토록 하는 것 같습니다. ex모드가 행단위로 동작한다는 것은 알고 계시리라 생각 합니다. :/pattern/형식으로 입력을 하면 패턴이 있는 행으로 이동을 하지요. 이것이 어떻게 쓰일 수 있을지 생각해 보시기 바랍니다. ^^ 위에서처럼 찾게 되면 :/pattern/ 찾은 곳으로 이동만 하게 됩니다. 여기에 몇 가지 명령과 옵션을 덧붙여서 조금 복잡한 찾기를 할 수 있습니다. 간단한 것부터 예를 들죠. :=를 입력해 봅시다. 현재 커서가 위치한 행의 번호가 출력이 됩니다. :g/pattern/을 해 봅시다. 패턴이 나타난 줄의 내용을 모두 표시하면서 마지막에 나 타난 행으로 이동할 것입니다. 다시 :/pattern/= 이나 :g/pattern/=을 입력해 봅시 다. 앞에 것은 처음 pattern이 나타난 행 번호만 표시를 합니다. 두 번째 것은 pattern이 나타나는 행 번호를 모두 표시를 합니다.

2.3. 치환

제가 vi를 쓰면서 감동(TT)을 받는 것이 바로 이 치환 명령의 강력함입니다. 일반적인 치환문의 형식은 다른 ex명령들과 마찬가지로 [범위] /old/new/ [옵션] 의 형식을 사용합니다. 여기서 한가지 보충 설명을 하고 넘어가야 하는 것이 있습니다. hold buffer 라는 것인데 검색을 하면서 검색된 문자열을 보관하는 일종의 기억장소입니다. 사용 법은 검색할 문자열을 \( \)로 둘러싸면 됩니다. 그럼 앞에서 순서대로 \1 ~ \9까지 의 버퍼에 찾은 문자열이 복사가 되지요. 비슷한 기호로 &가 있는데 이는 찾아낸 문 자열을 나타냅니다. 또 한가지 ~기호인데 이것은 바로 전에 수행한 검색이나 치환의 정규표현식을 나타냅니다. 예를 들어서 설명해 보도록 하죠. 우선 문서가 하나 필요하군요... 이전에 사용한 문서를 다시 사용하도록 하죠.
# /etc/bashrc

# System wide functions and aliases
# Environment stuff goes in /etc/profile

# For some unknown reason bash refuses to inherit
# PS1 in some circumstances that I can't figure out.
# Putting PS1 here ensures that it gets loaded every time.
PS1=[\u@\h \W]\\$ 

alias which=type -path
:%s/#/_&_/g라고 하면 #가 모두 _#_바뀌어 있을 것입니다. 찾는 문자열이 길 어지는 경우에 &를 사용하면 간단하겠지요? 이번엔 위에서 언급한 hold buffer를 사 용해 봅시다. :%s/\(goes\) \(in\)/\2 \1/g 이렇게 입력하면 어떻게 바뀔까요? goes in을 찾아서 각각 hold 버퍼 1, 2 에 저장을 해 둡니다. 뒤에서 치환할 때 버퍼 이름 이 순서가 바뀌어 있으므로 그 내용인 in goes를 출력하게 됩니다.
# Environment stuff goes in /etc/profile 명령 전 
# Environment stuff in goes /etc/profile 명령 후

2.4. 정리 및 연습문제

몇 가지 설명을 하지 않은 것들이 있습니다. 위의 예제에서처럼 설정 파일들 은 문서 내에 / (역슬래쉬)기호들이 많이 있어서 패턴을 찾는데 쓰는 형식의 /과 혼 동될 수 있습니다. 물론 원래의 문자를 의미할 때는 \/로 모두 대치하면 되지만 이런 경우에는 헛갈리죠. /대신 기호나 :을 사용해서 각각을 구분 짓습니다. :%s:/etc/:/etc/local/:g를 하면 /etc/를 /etc/local/로 치환을 하게 되죠. 위에서 배운 것들의 조합으로도 많은 패턴을 찾고 수정을 할 수 있습니다. 예를 들어보도록 하지요. 연습문제로 다음이 어떤 일을 할지 생각해 보시고 풀이를 보시기 바랍니다. 아래 (1)과 (2) 번은 위에서 언급을 하지 않았지만 해보시면 어떤 의미를 갖는지 아실 수 있으리라 생각합니다.
  연습문제 3. 다음 치환문의 의미를 설명하시죠.... ^^ 
    
:%s/.*/\U&/g 
:%s/.*/\L&/g 
:%s/\(.*\) *$/\1/ 
:%s/^ \+\(.*\)/\1/ 
:%s/^ \+\|^\t\+\(.*\)/\1/ 
:%s/^M//g (여기서 ^M은 CTRL-V후에 [ENTER]를 누른 값) 다음 
명령의 의미를 설명하세요. 
:n out.txt | g/pattern/, .w >> out.txt (out.txt화일이 존재해야 
합니다.) 참고) :g/pattern/y A 
:g/pattern/d