vi. 한 걸음 더

    저자 : 이 승 현 (Hitel : Justus)  
     

 

 

    한 달 동안 안녕히 지내셨습니까. 그동안 vi하고도 많이 친해지셨는지요. 만일 동호회 자료실이나 인터넷에서 윈도우95용 vi를 찾아다닐 정도까지 되셨다면 상당히 성공적이라고 할 수 있겠는데 어떠신지요. 도스의 EDIT나 메모장을 쓰다가도 무심코 j키를 눌러대며 커서가 아래로 내려갈 거라 생각한 적은 없나요. 아니면 <Ctrl>-S 대신 :w<CR>을 입력한 적은? 열심히만 하신다면 vi 고수들이나 저지를 수 있는 이런 실수들을 여러분도 곧 경험하시게 될 겁니다.
    지난 호에서 익힌 내용들은 "나 vi 쓸 줄 안다" 고 할 수 있는 정도의 수준이었습니다. 그 정도면 문서를 읽고 쓰는데 별 불편이 없을 겁니다. 실제로 많은 사람들이 그 정도까지만 익히고 맙니다. MS Word나 hwp도 대부분 기본적인 기능만을 쓰고 일부만이 모든 기능을 충분히 활용하는 것과 마찬가지입니다. 사실 GUI가 일반화되고 사용이 쉬워지면서 개발자는 더 많은 노력을 기울이는 반면 사용자는 게을러지는 경향이 있습니다. 리눅서라면 한가지 프로그램을 쓰더라도 이 잡듯이 뒤져서 충분히 활용하는 자세를 가져야겠지요? 그래서 이번 호에서 다룰 내용은 vi를 더 능률적으로 쓰기 위한 기능들로 정했습니다. 지난 호 못지 않게 주의를 기울여서 익히시기 바랍니다. 지금까지 내용이 강호에 나가 고수들과 대적해도 목숨을 부지할 수 있는 수준이었다면 이번 호를 마치면 일파의 장문인이 되기에 손색이 없는 정도의 실력을 갖출 수 있게 될 것입니다.
    우선 공부할 준비를 합시다. 설마 까만 바탕의 콘솔에 vi만 하나 달랑 띄워 놓은 건 아니겠지요. 있는 걸 활용 못하는 것도 (큰) 죄입니다. 우선 맘에 드는 윈도우 매니저를 설정하고 X 윈도우를 띄우세요. vi를 쓰기 위한 xterm 하나 올리고, 또...mp3나 CD player 같은거 하나 여는 것도 잊지 마세요. 크리스마스가 이제 채 1년도 남지 않았으니 캐롤을 틀거나 아니면 요즘 잘 나가는 김경호도 좋겠지요. 설운도로 하시겠다구요? 아직 이런 걸 할 줄 모르신다면 우선은 썰렁한 vi 화면 하나로 시작할 수 밖에요. 일기예보에는 곧 눈이 내릴거라 하네요. 강원도 산간에는 벌써 허리까지 쌓였답니다. 오늘 공부가 끝날 무렵이면 함박눈을 볼지도 모르겠네요. 자, 이제 됐지요?
    리눅스 개발자를 위한 운영체제라는 말은 익히 들어 아실터입니다만, 여러분 중 상당수는 프로그래밍을 해보신 적이 없을 겁니다. 아이러니한 일이지만 요즘은 연구나 개발보다는 전자오락을 하는데 필요한 사양이 훨씬 더 높더군요. 만일 갖고 있는 시스템이 신통치 않다면 퀘이크2 화면 넘어가는 거 보면서 한숨 쉬는 것보다는 프로그래밍하는 쪽이 좋을 겁니다. 둘 다 시간 때우기도 마찬가지이고 때로는 밤을 샐 수도 있으니까요. 이제, 그림 1을 보면서 역사상 가장 유명하고 또 가장 단순한 프로그램을 하나 만들어 보기로 하지요.

    <그림 1. hello.c 만들기>
    #include <studio.h>

    main()
    {
                printf("Hello, WORLD.\n");
    }
    ~
                  
    :e hello.c<CR>5G4w5~
    ~
    ~
    "hello.c" [New File} 6 lines, 59 characters written

    음... 상당히 썰렁하지요? 하지만 이것도 엄연히 프로그램입니다. 수십 개의 파일로 이루어진 커널 소스와 마찬가지로, 이번에는 Makefile을 만들어 봅시다. 헬로 월드에 무슨 메이크파일이냐고, 배보다 배꼽이 더 큰 격이라고 하실지 모르지만 우리 한번 구색을 갖추고 폼 한번 잡아 보자고요. 일단 vi를 빠져 나온 후 셸 프롬프트에서 다시 vi Makefile<CR>이라고 하면 되겠지만, 이건 고수가 할 바가 아니지요. 이제부터는 체통을 지켜야 합니다. 가벼이 움직여서는 안된다는 말이지요. 명령 모드에서 그림 2를 보세요.

    <그림 2. :e 명령으로 새 파일 열기>
    ~
    ~
    ~
    ~
    ~
    "Makefile" [New File]

    <CR>은 지난 호에서와 마찬가지로 그냥 엔터키를 치라는 의미입니다. 혹시 첫 번째 문서가 저장되지 않았다는 경고 메시지가 나왔다면 저장한 후 다시 해보세요. 저장하는 법은 기억 나시나요? 이 글의 앞 부분에 힌트가 있으니 잊어 버린 분은 찾아보시고, 자, 이제 두 번째 파일을 다음과 같이 작성하세요.

    <그림 3. Makefile 내용>
    CC = gcc

    hello: hello.o
               $(CC) hello.o -o $@
    ~
    ~
    ~
                            
    :w<CR>
    ~
    ~
    "Makefile" [New File] 4 lines, 45 characters written

    여기서 $(CC) 앞의 공백은 스페이스 대신 탭 키를 써야 합니다.
    이제 말로만 듣던 컴파일이란 걸 한번 해볼까요? vi를 끝내고 나오냐구요? 그림 4를 보시지요.

    <그림 4. :sh 명령>
    ~
    ~
    ~
    ~
    ~
    :sh
                          
    <CR>
    ~
    ~
    ~
    ~
    ~
    $

    앗! 셸 프롬프트다. :sh 명령으로 vi 편집 화면이 사라지고 vi로 들어가기 전의 셸 프롬프트 상태가 되었군요. 그림 5를 보시면 이제 우리가 무엇을 해야 하는지를 알 수 있을 겁니다.

    <그림 5. make 실행하기>
    $make
    gcc     -c hello.c -o hello.o
    gcc hello.o -o hello
    $

    make  명령을 입력하면 컴파일과 링크가 끝나고 hello라는 실행 파일이 만들어질 겁니다. 이제 ./hello<CR>이라고 입력해보세요. 그냥 hello<CR>이라고 입력할 경우, 환경 설정에 따라 실행될 수도 안될 수도 있습니다. 앞으로도 현재 디렉토리에 있는 파일을 실행할 때는 ./를 붙이는 것을 습관화하시기 바랍니다. 이번에는 ls 명령으로 현재 디렉토리에 어떤 파일들이 있나 보세요. hello.c와 Makefile은 직접 편집했던 것이고 hello는 make가 만든 것입니다. 먼저 hello.c 라는 파일이 있을 겁니다. 이것도 make가 만든 것입니다. 먼저 hello.c로부터 hello.o를 만들고 그 다음에 hello.o로부터 hello를 만듭니다. 첫 번째 과정을 컴파일(compile), 그 다음은 링크(link)라고 하지요. 이제 원래의 vi 편집 화면으로 되돌아가 보겠습니다. 이 때 주의해야 할 것이 있습니다. :sh 명령은 vi 편집을 중단하고 vi 안에서 셸을 실행하라는 명령입니다. 이것을 잠깐 잊어 버리고 다시 vi를 실행하라는 명령입니다. 이것을 잠깐 잊어 버리고 다시 vi를 실행하는 경우가 있습니다. vi 안에서 또 vi를 실행시키는 것이지요. 별 문제가 없을 수도 있지만 만일 이미 열어 놓은 문서를 다시 열어 놓는다면 심각한 문제가 생길 수도 있습니다. 이것은 서로 다른 가상 터미널에서 동일한 파일을 열어 놓는 경우에도 마찬가지입니다. 예를 들어, 두 개의 xterm에 같은 파일을 vi로 열어 놓고 편집하는 경우를 가정해 봅시다. 한 터미널에서 편집한 후 저장합니다. 다시 처음의 터미널에 열린 채로 있는 파일을 편집한 후 저장합니다. 그러면 어떤 결과가 얻어질까요? 짬뽕 한 그릇? 결국 중간에 편집한 내용들은 모두 잃어 버리게 됩니다. 다행히 요즈음의 vi들은 한 파일을 동시에 열 경우 경고 메시지를 주는 것도 있습니다만, 그렇지 않은 경우에는 특히 조심해야 합니다. 그렇다면 어떻게 원래의 편집화면으로 돌아올 수 있을까요? 여러분이 처음 로긴 했을 때 셸 프롬프트를 보게 되는 것은 로긴 후 처음 실행하는 프로그램이 sh, csh, tcsgm bash와 같은 셸 프로그램으로 설정되어 있기 때문입니다. 로그아웃을 하기 위해서는 셸 프롬프트 상태에서 exit 명령이나 <Ctrl>-D를 입력해서 실행 중인 셸 프로그램을 종료시키지요. 이때도 마찬가지입니다. 이제 빠져 나오세요.
    vi 편집 도중에 셸 프롬프트 상에서 잠깐 작업을 하기 위해서는 vi를 빠져나오기 보다는 이와 같은 방법이 더 효과적입니다. 별 차이가 없어 보입니까? 과연 그럴까요? 지난 호에 익힌 내용 중 "a, "b와 같이 특정 버퍼에 단어나 문장을 저장한 후 문서의 다른 부분에 붙여 넣는 방법을 설명 드렸지요. 만일 vi를 빠져나오면 이미 버퍼에 지정했던 내용이 모두 날아가버립니다. 하다 못해 커서의 위치도 잊어 버리기 때문에 복잡한 문서라면 이전에 편집하던 위치를 찾아가는 것만도 상당히 귀찮은 일이 될 겁니다. 그렇지만, vi를 중단하지 않은 상태에서 :sh를 쓰면 이와 같은 모든 작업 환경이 고스란히 유지됩니다. 이외에도 셸 프롬프트 상에서 입력할 명령어가 하나 뿐인 경우, 더 간단히 할 수 있는 방법이 있습니다. 그림 6을 보세요.

    <그림 6. :!를 이용한 명령어 실행>

                         
    :!make<CR>
    $exit
    exit

    make: 'hello' is up to data
    Press RETURN or enter command to continue

    첫 번째 줄은 컴파일할 필요가 없다는 메시지로 make 유틸리티가 준 것입니다. make는 어떤 명령어(이 경우, gcc)의 입력 파일과 출력 파일의 수정 시간을 서로 비교하여 컴파일(또는 링크)이 필요한지를 결정합니다. 두 번째 메시지는 vi가 준 것입니다. 시키는 대로 엔터키를 치면 원래의 편집 화면으로 돌아올 겁니다. 정말 make가 그렇게 똘똘한 놈인지 간단히 확인하고 넘어갈까요? 일단, 그림 7에서와 같이 파일의 내용을 좀 고쳐 보겠습니다.

    <그림 7. hello.c의 수정>

                         :e hello.c<CR>5G4w5~

    #include <stdio.h>

    main()
    {
                    printf("Hello, WORLD.\n");
    }
    ~
    ~
    ~
    "hello.c" 6 lines, 59 characters

    ~는 지난 호의 끝 부분에 설명 없이 슬그머니 나왔다 들어간 명령입니다. (숨은 그림 찾기라고 생각하시고 찾아보세요.) 대문자는 소문자로, 소문자는 대문자로 바꾸어 주는 명령입니다. 지난 호에 열심히 공부한 분들은 이미 알고 계실 겁니다. 앞에 붙은 숫자 5가 무슨 역할을 하는지도 물론 아시겠죠? (모르시겠다고요? 뒤에 0을 붙이면 당신의 IQ가 됩니다. 히히)
    이제 다음과 같이 입력하세요.

    <그림 8. make 다시 실행하기>
       
                            :w<CR>:!make<CR>

    make: 'hello' is up to data
    Press RETURN or enter command to continue
    gcc      -c hello.c -o hello.o
    gcc hello.o -o hello
    Press RETURN or enter command to continue

    예상했던 결과가 나왔나요? 이번에는 make가 컴파일을 다시 했지요? 우리의 프로그램 hello는 하나의 파일로 이루어진 간단한 프로그램이기 때문에 사실 make가 얼마나 유용한지 느낄 수 없습니다. 그러나 리눅스의 커널 소스를 생각해보세요. 펜티엄 프로에서도 컴파일하는데 몇 분이 걸리는 프로그램을 파일 하나하나 직접 컴파일하고 링크한다면? 커널 컴파일할 엄두를 못 내겠지요? 다음으로 넘어가기 전에 앞서 잠깐 설명한 :e 명령에 대해 좀 더 알아보기로 하지요. 만일 방금 전 hello.c를 수정한 후 다시 Makefile의 내용을 보고 싶다면 :e Makefile<CR>이라고 하면 됩니다. 그런데 수정한 내용을 저장하지 않고 이 명령을 내리면 vi는 :q<CR> 명령을 내렸을 때와 마찬가지로 수행을 거부합니다. 이때의 처리 방법도 또한 마찬가지입니다. :q!<CR>이라고 했던 것처럼 :e! Makefile<CR>이라고 하면 됩니다. 형식상 단지 Makefile이라는 인수가 있고 없는 차이가 있을 뿐입니다. :e 명령에는 특이한 형태가 있는데 :e #<CR>이 그것입니다. 바로 전에 편집했던 파일을 부르는 명령입니다. 우리의 경우와 같이 두 파일을 열어 놓고 쓰는 경우에는 :e#<CR>로 두 파일 사이를 오갈 수 있습니다. 굳이 :e hello.c<CR>과 같이 파일 이름을 다 써줄 필요가 없는 겁니다. :e는 원래 새로운 vi 세션(session)을 시작하라는 의미입니다. 뒤에 파일 이름을 인수로 줄 경우 다른 파일을 열라는 의미가 되지만, 그냥 :e<CR>이라고만 하면 사실 별 의미가 없지요. 그러나 :e!<CR>은 상황에 따라 가시적인 변화를 가져올 수 있습니다. 이미 언급한 바와 같이 :e! Makefile<CR>과 같이 쓸 경우, 현재 편집 중인 파일에서 아직 저장하지 않은 내용을 버리고 다른 파일<Makefile>을 편집하라는 의미로 쓰이게 됩니다. 그렇다면 파일 이름을 인수로 주지 않고 :e!<CR>이라고 하면 무슨 뜻이 될까요? 그냥 아직 저장하지 않은 내용을 버리고 이전에 최근에 저장했던 상태로 되돌아가려면 :q!<CR>로 vi를 빠져 나온 후 다시 vi로 들어가면 된다고 했지요? 이제 간단히 :e!<CR>만으로 같은 결과를 얻을 수 있습니다. 앞으로는 :e!<CR>을 쓰세요. 물론 저장하기 전에요.
    이제 검색 기능에 대해 알아보겠습니다. 검색은 크게 한 줄 안에서의 검색과 문서 전체에 걸친 검색으로 나누어 볼 수 있습니다. 한 줄 안에서는 특정 문자를 지정해서 검색하는 것이 효율적이겠고, 여러 줄을 대상으로 할 때는 특정 단어로 검색하는 것이 효율적이겠지요. 이제 지난 호에서 여러분의 홈디렉토리에 복사해 두었던 Installation-HOWTO라는 문서를 열어보세요. 그림 9와 같이 입력해서 34번째 줄로 가보겠습니다. (Installation-HOWTO의 저번에 따라 조금 다를 수도 있습니다.)

    <그림 9. Installation-HOWTO 문서>

    여기서 우리 Linux라는 단어를 찾아보기로 하지요.
    그림 10의 예를 보세요.

    <그림 10. 오른쪽으로 찾기(f 명령어)>

    한번 더 해볼까요? 그림 11에서 보인 것처럼 같은 걸 다시 찾는 건 더 간단합니다.

    <그림 11. ; 명령어에 의한 반복 검색>

    왼쪽 방향으로 찾기 위한 명령어도 있습니다.

    <그림 12. 왼쪽으로 찾기(F 명령어)>

    이 경우에도 ; 명령을 쓸 수 있습니다.
    단, 이번에는 왼쪽 방향으로 검색을 반복합니다. 즉, ;는 검색하던 방향으로 반복합니다. 반대로, 검색하던 방향의 반대 방향으로 반복하는 명령도 있습니다. ;에서 점 하나 뺀 ,입니다. 다음 예를 보시죠.

    <그림 13. , 명령에 의한 반복 검색>

    한 줄 안에서 검색하는 명령어가 벌써 4개나 되네요.
    그렇지만 사실 한 줄 안에서 검색하는 경우는 그렇게 흔하지 않을 겁니다. 라인 프린터처럼 한 줄에 136문자가 나오는 게 아닌 다음에야 명령어를 쓰느니 그냥 눈으로 보고 h키와 l키로 찾아가는 게 속편할 지도 모르지요. 실제 유용한 것은 문서 전체를 뒤져보는 경우입니다.
    Installation-HOWTO에는 Linux라는 단어가 187번 나옵니다. (할 일 없는 분은 직접 세어보세요) 이걸 하나하나 찾아보겠습니다. 다음 예를 보세요.

    <그림 14. / 명령을 이용한 검색>

    한 줄 안에서의 문자 검색에도 ;과 ,라는 반복 명령이 있었지요. 여기에도 물론 있습니다. 다음을 보실까요?

    <그림 15. n 명령에 의한 반복 검색>

    n의 대문자 형태인 N은 반대 방향으로 반복 검색하라는 명령입니다. 이제 문서 전체를 Linux라는 단어를 찾아서 자유자재로 돌아다닐 수 있게 되었습니다. 한 줄 안에서의 문자 검색에도 각각 앞으로, 뒤로, 같은 방향으로, 반대 방향으로 찾는 4개의 명령이 있었으니까, 이 경우에도 현재 커서 위치에서 위로 거슬러서 찾는 명령이 있어야 공평하겠지요?

    <그림 16. N 명령에 의한 반복 검색>

    그림 17과 같이 /대신 ?를 써보겠습니다.

    <그림 17. ? 명령을 이용한 검색>

    정리를 하면, 여러 줄에 걸쳐 단어(또는 문자)를 찾는 명령 /,?,n,N은 한 줄 안에서 문자를 찾는 명령어 f, F, ;, ,는 에 대응되는 기능을 갖고 있습니다. 즉, 만일 ?를 이용해서 검색을 했다면, n을 누르면 문서를 계속해서 거꾸로 검색하고 N을 누르면 비로소 그 반대 방향인 위에서 아래로 검색하게 되는 겁니다.
    따라서 ,와 N은 모두 검색 방향을 반대로 트는 역할을 한다고 보시면 됩니다. 두 가지 검색 방법의 차이를 하나만 얘기해 본다면 검색의 범위를 들 수 있습니다. 한 줄 안에서의 검색의 경우에는 더 이상 검색의 대상이 발견되지 않으면 벨 소리를 내고 검색을 중단합니다. 문서 전체를 대상으로 할 경우에는 둘 중 하나를 선택할 수 있습니다. 하나는 현재 커서 위치에서 검색 방향에 따라 문서의 끝이나 처음에 도달하면 중단하는 것입니다. 나머지 하나는 문서의 끝에 도달했을 경우에는 문서의 처음으로 되돌아가서 현재 커서의 위치까지 검색을 계속하고 반대로 문서의 처음에 도달했을 경우에는 문서의 끝으로 되돌아가 현재 커서의 위치까지 검색을 계속하는 것입니다.
    이와 같은 검색 방식을 간단히 wrapscan이라고 합니다. 지난 호에서

    :set nu<CR>

    과

    :set nonu<CR>

    을 익혔던 기억이 날 겁니다. 마찬가지로

    :set wrapscan<CR>

    와

    :set nowrapscan<CR>

    을 이용하여 이 두가지 검색 방법 중 하나를 택할 수 있습니다. number를 nu라고 했던 것처럼 wrapscan을 줄여서 ws라고 할 수 있으므로

    :set ws<CR>

    와

    :set nows<CR>

    라고 해도 같은 결과를 얻게 됩니다.
    /나 ?를 이용한 검색에서는 문자뿐 아니라 단어는 물론 문장을 지정해서 검색할 수 있습니다. <Space>키도 쓸 수 있으니까요. 그러나 그것보다 더 편리하고 강력한 기능이 있는데 바로 패턴 매칭에 의한 검색 방법입니다. 패턴 매칭에 쓰이는 문자를 매직 문자라고도 하고 이때 쓰이는 표현 양식을 정규식(regular expression)이라고 합니다. 정규식은 유닉스 환경에서 매우 광범위하게 쓰이고 있기 때문에 이걸 얼마나 잘 이해하고 있느냐는 아주 중요합니다. 유닉스에 관한 책이라면 어느 것을 막론하고 자세히 다루고 있으므로 여기서는 간단히 설명을 드리지요. 만일, 187번이나 나오는 Linux라는 단어 중에 줄의 처음에 나오는 것만 찾아보고 싶다면 어떻게 하면 될까요? 이건 흔히 부딪히는 무제입니다. 정규식에 이용되는 문자 중에서 ^는 이런 용도로 쓰입니다. 지난 호에서 줄의 처음으로 가는 명령어로 쓰였지요? 정규식에서는 각 줄의 처음을 나타내는 문자로 쓰입니다. 그럼

     /Linux<CR>

    이라고 하면 될까요? 보통은 이걸로 충분하지만, 글쎄요. 한번 해보시지요. 어쩌면 원하는 결과가 나오지 않을지도 모릅니다. 여러분은 어떤지 모르지만 제가 가지고 있는 Installation-HOWTO는 모든 줄의 왼쪽이 <Space>문자 두 개씩을 여백으로 갖고 있기 때문에 검색 결과, 하나도 없다고 나오네요.

    <그림 18. 정규식의 활용 예>

    물론, 그림 19에 보인 것처럼 정규식에 <Space> 문자 2개를 포함시켜 주면 됩니다.

    <그림 19. 정규식의 활용 예(2)>

    그런데, 만일 <Space> 문자가 항상 두 개씩 들어간다는 보장이 없을 때는 어떻게 할까요? 해결 방법이 없을까요? 일단 더 진행해보도록 하지요. *도 패턴 매칭 문자 중 하나입니다. 셸 프롬프트 상에서 파일 이름을 나타낼 때에도 임의의 문자를 *로 나타내지요? 여기서도 비슷한 의미를 갖습니다만, 아주 중요한 차이가 있습니다. 셸 프롬프트 상에서 파일 이름을 나타낼 때는 *가 임의의 문자가 0번이상 반복되는 것을 의미합니다. 그렇지만 이 경우에는 바로 앞에 있는 문자가 0번 이상 반복되는 것을 의미합니다. 따라서 셸 프롬프트 상에서 *가 갖는 의미와 같은 용도로 쓰려면 .*라고 표현해야 합니다. .는 정규식에서 임의의 한 문자를 나타내거든요. 셸 프롬프트 상에서 파일 이름을 나타낼 때는 ?가 임의의 한 문자를 의미한다는 것도 알아두세요. 절대 혼돈하시면 안됩니다.
    이제 아까 우리가 해결하지 못했던 문제를 다시 생각해보기로 하지요. 줄의 처음에 나오는 Linux라는 문자를 정규식으로 어떻게 표현하면 될까요?

    ^*Linux

    이게 무엇을 뜻하는지 한번 뜯어보기로 하지요. 우선 맨 앞의 ^는 물론 줄의 시작을 나타냅니다. 이것이 줄의 중간에 나오는 Linux라는 단어를 검색 대상에서 제외시켜 주지요. 그 다음에는 <Space>문자가 하나 있네요. 이보다는 그 뒤에 따라오는 * 문자가 중요한데, 이건 바로 앞에 있는 문자(<Space>)가 0번이상 반복된다는 걸 의미합니다. 없을 수도 있고 하나 또는 여러 개일 수도 있다는 것이지요. 줄의 처음으로 가라는 명령어 ^가 정규식에서 줄의 처음을 나타내는 문자로 쓰인다면 줄의 끝으로 가라는 명령어 $는 정규식에서 줄의 끝을 나타내는 문자로 쓰이지 않을까요? 맞습니다. 그래서 vi는 배우면 배울수록 빨리 늘게 되는 겁니다. 그림 20을 보세요.

    <그림 20. 정규식의 활용 예(2)>

    이제 n 명령을 내릴 때마다 줄의 끝에 나오는 Linux만을 찾아줄 겁니다. 이번에는 좀 색다른 걸 해봅시다. (period)를 찾는 겁니다. 이미 언급한 바와 같이 .는 정규식에서 특수한 의미를 갖고 있습니다. 만일

    /.<CR>

    이라고 한다면 n을 누를 때마다 커서가 현재 위치부터 한 문자씩 진행할 겁니다. .에는 모든 문자가 해당되기 때문입니다. 한마디로 /.<CR>은 무의미한 명령입니다. 그러면 이와 같이 패턴 매칭 문자를 문자 자체로서 찾으려면 어떻게 해야 할까요?

    /\.<CR>

    라고 해보세요. \는 바로 뒤에 오는 문자를 정규식에서의 패턴 매칭 문자가 아닌 일반 문자로 처리하라는 의미입니다. 직접 해보시면 그 차이를 금방 알 수 있을 겁니다. /$<CR>과 /\$<CR>도 비교해 보세요. 전자의 경우 매 줄의 끝에서 커서가 멈추어 서지만 후자의 경우는 셸 프롬프트나 화폐 단위로 쓰인 $에서 멈추어 설 겁니다. 이건 어떨까요?

    /$ <CR>

    $와 엔터키 사이에 <Space>가 들어간 경우입니다. 이때는 $가 줄의 끝을 나타내기 위한 문자가 아니라는 것이 명백하지요. 왜냐하면 줄의 끝이라면 그 뒤에 아무 문자도 없어야 하니까요. 따라서 이때는 \를 쓸 필요가 없습니다.
    이와 같이 정규식 표현이 아주 중요하다는 것을 이미 말씀드렸지만 여러분 중에는 그런 복잡한 것은 죽어도 못쓰겠다는 분도 계실 겁니다. (어떤 사람은 "때려 죽여도 못한다"는 표현을 쓰더군요.) 그런 분들께는 패턴 매칭 문자의 존재 자체가 불편하고 귀찮을 겁니다. 앞에서처럼 .를 찾는 경우를 생각해보세요. 이런 분들을 위해 아예 패턴 매칭 문자의 기능을 없애 버리는 방법이 있습니다. 아까 패턴 매칭 문자를 매직 문자라고 한 적이 있는데 기억나십니까?

    :set nomagic<CR>

    라고 한 후 다시 /.<CR>을 해보세요. 이제 일일이 \를 쓸 필요가 없을 겁니다. 다시 원래 상태로 돌아가려면 물론,

    :set magic<CR>

    이라고 하면 됩니다.
    벌써 :set option<CR> 형태로 된 명령어가 여러 번 나왔군요. vi에는 환경을 설정하기 위한 옵션(option)이 여러 가지 있습니다. 우리가 지금까지 다룬 number, wrapscan, magic이 여기에 포함됩니다. 이들은 :set option<CR>과 :set nooption<CR>의 형태로 쓰입니다. 전자는 옵션을 켜는 것이고 후자는 끄는 것입니다. 옵션 이름이 길 경우에는 약자를 쓸 수도 있습니다. number 대신 nu, wrapscan 대신 ws 처럼 말이지요. 옵션 중에는 값을 가지는 것도 있습니다. 마치 셸 변수를 설정하는 것과 비슷한 방법으로 사용합니다. 예를 들면 보통 탭은 8문자 단위로 설정되어 있습니다. 탭 문자 하나가 8개의 <Space>처럼 보이게 되지요. 그런데 들여쓰기가 여러 단계로 된 프로그램 소스 같은 파일을 볼 때는 사실 8문자는 너무 큽니다. 전 이걸 3문자로 줄여서 쓰고 있는데,

    :set tabstop=3<CR>

    이라고 하면 됩니다.
    그밖에 autoindent라는 것도 유용합니다. 역시 들여쓰기를 여러 단계로 하는 경우, 새 줄을 만들 때 커서가 있던 줄에 맞추어서 들여 쓰기를 자동으로 해주는 기능입니다. 약자는 ai입니다.

    :set ai<CR>

    이라고 한 후에 들여 쓰기가 되어 있는 곳을 찾아 o나 O 명령을 써보세요. 이는 특히 프로그래밍 작업을 할 때 아주 편리한 기능입니다. 이와 같이 :set 명령에는 여러 가지가 있는데 현재 어떻게 설정되어 있는지를 알아보려면 :set<CR>을 하시면 됩니다. 이렇게 하면 디폴트값과 다르게 설정되어 있는 항목만을 보여줍니다. 만일 모든 설정값들을 보고 싶다면 :set all<CR>이라고 하면 됩니다. 우리가 배운 것들은 전체 중 극히 일부에 지나지 않는다는 것을 알 수 있을 겁니다. 물론, 모두가 중요한 것은 아니니 일단은 그냥 보기만 하고 넘어갑시다. 이렇게 많은 :set 명령을 vi를 띄울 때마다 일일이 입력해 주어야 한다면 보통 귀찮은 일이 아닙니다. 다행히도 vi가 실행될 때 자동으로 읽어들이는 환경 설정 파일이 있습니다. exrc인데, 보통 홈디렉토리에 둡니다. 현재 작업 중인 디렉토리에 둘 수도 있습니다. .으로 시작하기 때문에 그냥 ls 명령으로는 보이지 않습니다. ls -a라고 해야 보입니다. 여기에 다음 그림에서 보인 바와 같이 :로 시작하는 명령들을 :을 빼고 나열해 놓으면 됩니다.

    <그림 21. .exrc 파일의 예>
    set wrapscan
    set tabstop=3
    set noautoindent
    ~
    ~
    ~
    ~
    ~
    ".exrc" 3 line, 44 characters

    잠깐 장난 한번 쳐볼까요? .exrc파일 안에 q라고 해놓으면 어떻게 될까요? 지금 한번 해보세요. 그리고 vi를 띄워보세요. 만일 바로 셸 프롬프트로 빠져 나온다면,이제 다시는 vi를 쓸 수 없게 된 겁니다. vi가 뜨자마자 .exrc를 실행하는데 그 안에 종료 명령(q)을 넣어 놓았으니까요. 들어가자마자 :q<CR>을 누른 격이거든요. 물론 vi 중에는 .exrc 안에 있는 q를 무시하도록 되어 있는 것도 있지만, 여러분이 쓰고 있는 vi 가 고지식한 놈이라면 그대로 튀어나와 버릴 겁니다. 이제 어떻게 하느냐구요? 글쎄요. 각자 알아서 해결해보세요. 하하하.
    지난 호에 커서 이동 명령어로서 H, M, L을 익혔습니다. 화면은 그대로 있고 커서만 움직였지요. 반면 커서는 문서상의 현재 위치에 그대로 있고 화면이 스크롤하면서 움직이는 명령어도 있습니다. 문서가 스크롤 됨에 따라 커서도 함께 오르내리지요. <Ctrl>-E와 <Ctrl>-Y가 그것입니다. 앞서 배운 검색 명령어를 반복하다 보면 검색 방향에 따라 커서가 화면의 맨 밑이나 위로 가있게 됩니다. 원하는 위치를 찾은 뒤에는 화면을 옮겨서 찾은 위치를 화면의 중앙에 오도록 하는 것이 보기에 좋겠지요. 이때 <Ctrl>-E나 <Ctrl>-Y를 쓰는 겁니다. 이들은 한 줄씩 화면을 스크롤 시킵니다만, 5<Ctrl>-E와 같이 써서 여러 줄 단위로 할 수도 있습니다. 이미 지난 호에서 배운 <Ctrl>-U/D/F/B도 바로 같은 부류의 명령어들입니다. 다만, 커서가 스크롤 되다가 화면의 끝에 걸리면 커서는 더 이상 넘어가지 않고 화면만 넘어가는 점이 다를 뿐입니다.(커서가 사라져 버리면 안되겠지요?) 이밖에 화면을 움직이는 데는 특별한 명령이 있습니다. 다음 화면을 보시지요.

    <그림 22. 화면 이동 명령어 z<CR>>
    <그림 23. 화면 이동 명령어 z.>
    <그림 24. 화면 이동 명령어 z->

    커서가 있는 줄을 각각 화면 맨 위로, 화면 가운데로, 화면 맨 밑으로 가도록 화면을 옮겨줍니다. 이런 명령들은 검색 명령어와 함께 쓰면 문서의 내용을 훑어볼 때 훨씬 능률적으로 할 수 있을 겁니다. 화면 이동과는 별 관련이 없지만 화면 상태를 다루는 명령어로 <Ctrl>-L도 알아두시기 바랍니다. 간혹 시스템이 보내는 메시지가 편집 중인 화면 위에 뜨는 경우가 있습니다. 대표적인 경우가 외부에서 talk를 걸어오는 경우 등이 있습니다. 아니면 수행중인 데몬 프로그램이 보내는 에러 메시지가 뜰 수도 있지요. 이런 경우, 실제 편집 중인 내용에는 전혀 영향을 주지는 않지만 화면상에는 편집 중인 내용과 메시지들이 섞여서 매우 혼란스럽게 되지요. 이럴 때 쓰는 명령입니다. 화면을 다시 그려 달라는 것이지요.
    Installation-HOWTO는 1194줄이나 되는 문서입니다. 여러분이 만약 디스크 파티션에 관한 부분을 찾아 읽어보고 싶다면 /partition으로 찾아보면 되겠지요. 그런데 이런 식으로 문서 안을 헤집고 돌아다니다 보면 방금 전에 보던 곳을 다시 찾아가고 싶을 때가 생깁니다. partition이란 단어가 한두 번 다시 검색해서 금방 찾을 수 있겠지만 partiton은 170번이나 나오는군요. 지난 호에 배운 <Ctrl>-G를 이용해서 현재 몇 번째 줄에 있는지를 기억해두는 것도 한가지 방법입니다. 길거리 간판에 붙어있는 전화번호 외우는 게 취미인가요? 진기명기에 나갈 정도의 기억력을 가지고 있지 않다면 일찌감치 포기하시기 바랍니다. 똑똑한 컴퓨터 놀리면서 내 머리 혹사시킬 필요는 없겠지요? 이럴 때 요긴하게 쓸 수 있는 기능이 있습니다. 마킹이라는 겁니다. 문서 안의 특정한 위치를 기억시켜 두었다가 한번에 찾아가는 기능입니다. 다음 예는 실제적인 경우라기 보다는 참고를 위한 것입니다.

    <그림 25. 마킹의 활용 예.>

    ma는 현재 커서위치를 a로 마킹하라(표시하라)는 의미입니다. 마찬가지로 mb는 b로 마킹하라는 의미입니다. 지난 호에서 "a로부터 "z까지 26개의 이름 붙은 버퍼를 지정할 수 있다고 했었는데 마킹의 경우도 ma부터 mz까지 26개 위치를 지정할 수 있습니다. 일단 마킹을 한 후에는 a명령으로 찾아갈 수 있습니다. 위의 그림에서 보는 바와 같이 정확히 마킹된 문자의 위치로 찾아갑니다. 'a라는 명령도 있습니다. 아주 헛갈리는 일이지만, 이것도 마킹해 둔 위치를 찾아가기 위한 것입니다. 이 경우, 정확히 마킹된 문자를 찾아가는 것이 아니라 미킹된 문자가 있는 줄을 찾아간다는 차이가 있습니다. 실제로 a라고 하면 커서가 정확히 그 문자를 찾아가지만, 'a라고 하면 그 문자가 있는 줄의 맨 처음으로 갑니다. 다시 한번 말씀드리지만 '와'를 반드시 구분하시기 바랍니다. 앞의 것은 그라브 액센트(grave accent)라고 하는 것이고 뒤의 것은 우리가 따옴표로 흔히 쓰는 기호입니다. 보통 PC용 키보드에서는 왼쪽에, '는 오른쪽에 위치하고 있습니다. 유닉스 셸을 잘 쓰시는 분이라면 셸에서도 두 개의 쓰임새가 서로 다르다는 것을 아실 겁니다. 지난 호에서 익혔던 복사 및 붙여 넣기 기능을 마킹과 연결해서 예를 들어 보겠습니다.

    <그림 26. 마킹의 활용 예(2)>

    그림에서 보는 바와 같이, 마킹은 현재 커서가 있는 위치에 대해 이루어지지만, y나 d와 같은 명령과 함께 쓰일 때는 `가 쓰이느냐 또는 '가 쓰이느냐에 따라 그것이 문자 단위로 적용되느냐 줄 단위로 적용되느냐가 결정됩니다. 한가지 예를 더 들어보겠습니다.

    <그림 27. 마킹의 활용 예(3)>

    마킹된 위치는 같은데 처음의 경우는 문자 단위로 지워지고 두 번 째 경우는 줄 단위로 지워졌다는 것을 알 수 있을 겁니다. 마킹 정보는 새로운 vi 세션이 시작되면 사라지게 됩니다. 예를 들면 :e 명령을 사용하면 마킹 정보가 모두 사라집니다. "a~"z로 지정된 버퍼의 내용은 보존된다는 점과 비교해서 알아두시기 바랍니다. 버퍼에 대한 말이 나왔으니 몇 가지 더 말씀드리겠습니다. 우리가 "a~"z 까지 지정해주는 이름 붙은 버퍼와는 달리 기본 버퍼는 vi 세션이 바뀌면 원래 내용이 사라집니다. (어떤 vi에서는 보존되기도 합니다.) 그래서 :e 명령을 이용해서 여러 문서 사이를 오가며 편집을 할 때 버퍼의 내용을 보존하려면 원칙적으로 이름 붙은 버퍼를 써야 합니다. 버퍼에는 이름 붙은 버퍼 외에 번호 붙은 버퍼도 있습니다. 이건 그야말로 아주 특수한 내용을 담고 있습니다. "1로 지정되는 버퍼는 바로 직전에 삭제된 내용을 담고 있습니다. "2로 지정되는 버퍼는 그 전에 삭제된 내용을 담고 있습니다. 이런 식으로 "9로 지정되는 버퍼까지 9개의 특수한 버퍼가 제공됩니다. 여기에는 복사된 내용은 들어가지 않고 삭제된 내용만 들어갑니다. 원래 vi는 여러 단계의 취소(undo) 기능이 제공되지 않습니다. 대신 번호 붙은 버퍼를 이용하면 잘못 삭제된 내용을 되살릴 수 있습니다. 편집 중인 문서의 적당한 곳에 빈 공간을 만들고 그곳에서 "1p"2p"3p를 입력하면 최근에 지워진 내용이 3개까지 복원됩니다. 이중에서 필요한 내용을 취한 후 지워 버리면 되겠지요. 이 기능을 좀 더 편히 쓰게 하기 위한 배려가 있습니다. 잠깐, 여기서 숙제 검사 한 번 할까요? "야, 숙제가 뭐냐?" 웅성거리는 소리가 들리는군요.학교 다닐 때보면 꼭 그런 친구들이 있지요. 지난 호 끝 부분을 보세요. .가 무슨 명령어냐하는 것이었지요? 이건 바로 전에 문서의 내용을 변경한 명령을 반복하는 것입니다. 지우는 명령이든. 붙여 넣는 명령이든 문서의 내용을 바꾼 것이라면 어떤 명령이든 상관없습니다. (따라서, 검색 명령은 해당되지 않습니다.) 예를 들면, 한 단어씩 지워 나갈 때 dw를 계속 쓰면 됩니다만, 두 번째부터는 그냥 .라고 해도 된다는 겁니다. 그런데 이 기능이 번호 붙은 버퍼와 쓰일 때는 아주 별나게 동작합니다. 다음 예를 보세요.

    <그림 28. 번호 붙은 버퍼의 특성>

    ddoch
    copyleft
    Justus
    blueecho
    ssangkop
                       
    "add..."1p...
    ddoch
    ssangkop
    blueecho
    Justus
    copyleft

    파일의 내용이 완전히 뒤집어졌지요? 그냥 .만 찍었는데 말이지요. 잘 생각해보면 "1p 뒤에 찍은 세 개는 "2p"3p"4p 라는 것을 알 수 있습니다.
    즉, 번호 붙은 버퍼 이후에 반복되어 쓰이는 .은 버퍼의 번호를 하나씩 증가시키는 역할을 한다는 것입니다. 이건 번호 붙은 버퍼에만 적용되는 것입니다. "ap...의 경우에는 "a로 지정된 버퍼의 내용만 4번 붙여질 뿐입니다. 지금까지 다른 내용을 다 이해하셨습니까? 물론, 이해하는 것과 능숙하게 쓰는 것은 별개의 문제입니다. 저도 이 기능들을 모두 사용하고 있지는 않습니다. 또 사람에 따라 즐겨 쓰는 기능이 있고 전혀 쓰지 않는 기능이 있습니다. 다만, 자주 쓰지 않더라도 어떤 기능이 있는지에 대해 한번 정리해두면 언젠가는 필요할 때가 생기고 쉽게 찾아서 쓸 수가 있습니다. 이제는 vi 권법에 있어서 여러분과 능히 대적할 고수를 찾아보기 쉽지 앟을 겁니다. (그렇다고 아무한테나 시비 걸지 마시기를.) 다음 호에서는 여러분의 절정 고수의 경지에 이를 수 있는 비급을 정리해 드리지요.

      

    ▲ top

home으로...