Atomic Habits

[VIM/VI] 문자열 치환 옵션 본문

IT/Linux-Vim

[VIM/VI] 문자열 치환 옵션

체계성 2021. 12. 26. 13:49

출처 : http://gypark.pe.kr/wiki/Vi%EB%A1%9C%EB%AC%B8%EC%9E%90%EC%97%B4%EC%B9%98%ED%99%98%ED%95%98%EA%B8%B0

1. VIEditor

Vi 의 기본 사용법은 알아서 공부하자. :-) MS 윈도우에서 사용할 경우, ESC 를 누를 때 한/영 전환이 자동으로 영문모드로 바뀌지 않기 때문에 불편하다. [여기]를 뒤져보면 이 문제를 해결한 패치 버전이 있다.

참고:

2. 바꾸기 기초

Vi 에서 문자(열) 바꾸기는 콜론 모드에서 's'ubstitute 명령을 사용한다.

:(시작줄),(끝줄)s/찾을패턴/바꿀스트링/옵션
  • 시작줄, 끝줄 : 바꾸기를 할 범위를 행번호로 지정. "."는 현재 커서가 있는 줄. "$" 는 제일 마지막 줄을 의미
  • 찾을 패턴, 바꿀 스트링 - 말 그대로... 전자를 찾아 후자로 치환한다. 앞은 "pattern" 이고 뒤는 "string" 임에 주목. 가장 큰 차이점은, 전자에는 "정규표현식"을 사용할 수 있다는 것.
  • 옵션 :
    • g : global - 한 줄에 패턴이 여러 번 나오면 모두 바꾼다. 지정하지 않으면 첫번째 패턴만 치환
    • i : ignore case - 대소문자 구분을 하지 않는다.
    • c : confirm - 검색된 모든 문자열에 대해서 바꿀지 말지를 물어본다.

예:

:5,10s/a/b/     - 5번째 줄부터 10번째 줄까지 각 줄의 첫번째 "a" 를 "b" 로 바꾼다.
:.,.+10s/a/b/g  - 현재 줄부터 (현재 행번호+10)번째 줄까지 모든 "a" 를 "b" 로 바꾼다.
:1,$s/a/b/c     - 첫번째 줄부터 마지막 줄까지 (즉 문서 전체) 각 줄의 "a" 를 "b" 로 바꾸되, 사용자에게 확인을 받는다.
:%s/a/b/gi      - 역시 문서 전체에서 "a" 와 "A" 를 "b" 로 바꾼다.
:%s/Hello/Good Morning/g - 당연히... 두 글자 이상의 문자열도 검색 및 치환이 가능하다.

위의 바꾸기 기능은 웬만한 텍스트 에디터에는 다 있는 기능이다. 윈도우의 메모장에도 있다. 중요한 건 그 다음.

3. 정규표현식

정규표현식 (Regular Expression) 이란 무엇인가? 이 질문은 진짜 어렵다. 너도 나도 정규표현식을 얘기하지만 정작 정규표현식이 뭔지는 잘 안 나와 있다. (오토마타 수업을 들은 풍월로 굳이 얘기하자면 정규 문법에 의해 생성되는 정규 언어를 표현할 수 있는 표현식..이라고 하면 되려나) 어쨌거나, "하나 이상의 문자열을 한 번에 나타낼 수 있는 패턴"이 정규 표현식이다. 아래는 UNIX 서적이나 웬만한 웹사이트에 다 나오는 기본 정규 표현식이다.

a : 말 그대로 "a", b 는 당연히 "b", c 는...
. : 임의의 한 글자. 따라서 a.d 는 aad abd acd add aed afd...
[list] : list 중의 한 글자. 
  [adf] 는 a 또는 d 또는 f
  [a-f] 는 a, b, c, d, e, f
  [^adf] 는 a, d, f 를 제외한 나머지 중 한 글자. list 앞에 "^" 이 오면 뒤에 오는 것을 제외한 것을 의미한다.
  [^a-f] 는.. 말 안 해도 되겠지
 그러면 "^ 또는 a 또는 b"를 의미하고 싶을 때는? "^"를 list 의 제일 앞이 아닌 곳에 두면 된다.
  [ab^] - 마찬가지로 "-" 나 "]" 역시 [ab-] []df] 와 같이 쓴다.
* : 0번 이상의 임의 번 반복. a* 는 null string, a, aa, aaa, aaaa...

그런데 아무래도 저것만 가지고는 좀 불편하다. 그래서 "확장 정규 표현식"이 등장했다.

+ : 1번 이상의 임의 번 반복. a+ 는 a, aa, aaa, ... 즉 aa* 와 동일.
| : a|b 는 a 또는 b
() : group, (ab|cd)ef 는 abef 또는 cdef
? : 없거나 하나 있거나. ab? 는 a 또는 ab

위의 확장 정규 표현식은 Perl에서는 그냥 쓰면 되고, ViEditor 에서는 앞에 백슬래쉬를 붙여 a\+ 와 같이 사용한다.

임의 번 반복 대신이 구체적으로 숫자를 줄 수도 있다.

{n,m} : n번 이상 m번 이하 반복 (가능한 많이)
{n} : n번 반복
{n,} : n번 이상 반복 (가능한 많이)
{,m} : m번 이하 반복 (가능한 많이)
{} : 0 번 이상. * 와 동일

{-n,m} : n번 이상 m번 이하 (가능한 적게)
{-n} : n번
{-n,} : n번 이상 (가능한 적게)
{-,m} : m번 이하 (가능한 적게)
{-} : 0번 이상 (가능한 적게)

역시 vi 에서는 앞에 백슬래쉬를 붙여서 a\{2,5} 등과 같이 사용한다.

위에서 "가능한 많이"와 "가능한 적게"는 뭔가? abcdebcdebcde 라는 예로 들면, a.*d 는 abcdebcdebcd 에 매칭되고, a\{-}d 는 abcd 에 매칭된다.

참고:

4. 복수의 문자열을 하나의 문자열로 바꾸기

이것은 결국 위의 정규표현식을 사용한 패턴을 특정한 문자열로 바꾸는 것이다. 웬만한 vi 기초 문서에서 간단하게라도 다루는 내용이다.

:%s/[vV]i//g   - vi 또는 Vi 를 null string 으로 치환한다. 즉 삭제한다.
:%s/<.*>//g    - html 화일에서 태그를 제거하는 경우인데, 이렇게 쓰면 <b><i>내용</i></b> 라는 줄이 있을 때 죄다 지워질 것이다. 
                 따라서 <.*> 대신에 <.\{-}> 를 쓰는 게 낫다.
::%s/\(gnu\|Gnu\)/GNU/g  - gnu 또는 Gnu 를 GNU 로 치환

간단한 것이니 이 정도로 통과.

5. 복수의 문자열의 복수의 문자열로 바꾸기

사실 이 얘기를 하고 싶었던 것인데... 이것만 덜렁 쓰기가 뭣해서 서론이 장황해졌다.

핵심은, 괄호를 사용하여 찾는 문자열 쪽에 그룹을 지정한 후에, 바꿀 문자열 쪽에서 그 그룹을 부를 수 있다는 것이다.

\0 은 찾은 문자열 전체
\1 은 첫번째 괄호
\2 는 두번째 괄호
\3 은 세번째 괄호
...

괄호의 순서는 여는 괄호 "(" 의 순서로 따진다. 또 . 나 [list] 등이 괄호안에 있을 경우는 실제로 검색된 문자열을 의미한다는 것에 유의. 즉 abef 라는 스트링이 있고 \(ab\|cd\)ef 로 검색했다면 \1 은 ab 가 된다.

다음과 같은 경우를 생각해 보자. html 화일 안에 수십개의 링크가 다음과 같이 나열되어 있다.

aaa
bbb
ccc
...

이것을 내가 위키위키 페이지에 옮기려 한다. (사실 위처럼 깔끔하게 되어 있으면 그냥 html 코드를 써도 되겠지만)

* [http://www.aaa.com aaa]
* [http://www.bbb.com bbb]
* [http://www.ccc.com ccc]
...

다음의 한 줄로 만사형통.

:%s/<li><a href="\(.\{-}\)">\(.\{-}\)<\/a>/* [\1 \2]/g
Comments