UNIX/유닉스 공통

[스크랩] 정규 표현식 (Regular Expressions)

99iberty 2014. 5. 22. 17:11

http://croky.tistory.com/entry/%EA%B3%A0%EA%B8%89-Bash-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8C%85-%EA%B0%80%EC%9D%B4%EB%93%9C-%EC%B0%A8%ED%98%84%EC%A7%84%EB%8B%98%EA%B8%80-19-%EC%A0%95%EA%B7%9C%ED%91%9C%ED%98%84%EC%8B%9D

 

 

19장. 정규 표현식(Regular expressions)

쉘 스크립트를 완전하게 구사하기 위해서, 정규 표현식은 꼭 정복해야 합니다. 스크립트에서 자주 쓰이는 sedawk같은 명령어들이 정규 표현식을 사용합니다.

19.1. 정규 표현식의 간략한 소개

표현식(expression)이란 문자 그대로의 의미 이상으로 해석되는 메타문자(metacharacters)라고 부르는 문자들의 집합을 말합니다. 예를 들어, 인용 부호(quote symbol)는 어떤 사람이 말한 것을 나타내 주기도 하지만 또한 그 뒤에 나오는 심볼에 대해서 메타적 의미를 부여하기도 합니다. 정규 표현식은 유닉스에 특별한 특징을 부여하는 문자들과 메타문자들의 집합입니다. [1]

정규 표현식은 주로 텍스트 탐색과 문자열 조작에 쓰입니다. 정규 표현식은 하나의 문자와 일치(match)하거나, 혹은 문자열의 일부분(substring)이나 전체 문자열인 문자 집합들과 일치하게 됩니다.

  • 별표(*)는 바로 앞의 문자열이나 정규 표현식에서 0개 이상 반복되는 문자를 나타냅니다.

    "1133*"11 + 하나 이상의 3 + 가능한 다른 문자들 을 나타냅니다: 113, 1133, 11312, 기타 등등.

  • 점(.)은 뉴라인을 제외한 오직 한 개의 글자와 일치합니다. [2]

    "13."13 + 빈칸을 포함한 최소 한 글자 를 나타냅니다: 1133, 11333, 하지만 13은 뒤에 한 글자가 빠져 있기 때문에 아닙니다.

  • 캐럿(^)은 줄의 시작을 나타내지만 가끔 문맥에 따라서는 정규 표현식에서 문자 집합의 의미를 반대로 해석해 줍니다.

  • 정규 표현식의 제일 끝에 나오는 달러 표시($)는 줄 끝과 일치합니다.

    "^$" 는 빈 줄과 일치합니다.

  • 대괄호([...])는 단일 정규 표현식에서 표현하기 위해 문자들을 집합으로 묶어 줍니다.

    "[xyz]"x, y, z 중에 한 글자와 일치합니다.

    "[c-n]"c에서 n 사이에 들어 있는 한 문자와 일치합니다.

    "[B-Pk-y]"B에서 P까지 중이나 k에서 y까지 중의 한 글자와 일치합니다.

    "[a-z0-9]" 는 소문자나 숫자중의 한 문자와 일치합니다.

    "[^b-d]"b에서 d사이의 문자를 제외한 모든 문자를 나타냅니다. ^은 바로 뒤에 나오는 정규 표현식의 의미를 반대로 해석하게 해 줍니다(다른 문맥에서 !의 의미와 비슷함).

    여러개의 대괄호로 묶인 문자들은 일반적인 낱말 패턴을 나타냅니다. "[Yy][Ee][Ss]"yes, Yes, YES, yEs, 등등을 나타냅니다. "[0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9][0-9][0-9]"는 주민등록번호와 일치합니다.

  • 역슬래쉬(\)는 특수 문자를 원래의 문자 의미대로 해석하게 해줍니다(escape).

    "\$" 는 정규 표현식에서 줄 끝(end-of-line)을 나타내는 의미대신 "$" 문자 그대로 해석하게 해줍니다. 비슷하게 "\\"는 그냥 "\" 문자 그 자체를 나타냅니다.

  • 확장 정규 표현식. egrep, awk, 에서 쓰입니다.

  • 물음표(?)는 자기 앞에 나오는 정규 표현식이 0개나 한개인 것과 일치하고, 보통은 한 개의 문자와 일치할 때 쓰입니다.

  • 더하기(+)는 자기 앞에 나오는 하나 이상의 정규 표현식과 일치합니다. *와 비슷하게 동작하지만 반드시 하나 이상과 일치합니다.

    # GNU 버전의 sed와 awk에서는 "+"를 쓸 수 있지만,
    # 이스케이프(escape)를 해 줘야 됩니다.
    echo a111b | sed -ne '/a1\+b/p'
    echo a111b | grep 'a1\+b'
    echo a111b | gawk '/a1+b/'
    # 위의 세개는 모두는 동일합니다.
    # Thanks, S.C.

  • 이스케이프"중괄호"(\{ \})는 바로 앞에 나온 정규 표현식의 빈도수를 나타냅니다.

    중괄호를 이스케이프 시키지 않으면 중괄호 문자 그대로 해석되기 때문에 꼭 이스케이프를 시켜야 합니다. 이 방법은 기술적으로 볼 때, 기본적인 정규 표현식의 일부가 아닙니다.

    "[0-9]\{5\}" 는 0에서 9까지의 문자가 정확히 5번 나오는 것을 나타냅니다.

    경고

    "전통적"awk에서는 중괄호를 정규 표현식으로 쓸 수 없습니다. 하지만, gawk에서 --re-interval을 주면 중괄호를 이스케이프 시키지 않고도 정규 표현식에서 쓸 수가 있습니다.

    bash$ echo 2222 | gawk --re-interval '/2{3}/'
    2222
    	      

  • 소괄호인 ( )는 정규 표현식 그룹을 묶어줍니다. 다음에 설명할 "|" 연산자와 같이 쓰면 아주 좋습니다.

  • | "or" 정규 표현식 연산자는 가능한 문자들중 어떤 것과도 일치합니다.

    bash$ egrep 're(a|e)d' misc.txt
    People who read seem to be better informed than those who do not.
    The clarinet produces sound by the vibration of its reed.
    	      

  • POSIX 문자 클래스(POSIX Character Classes). [:class:]

    일치하는 문자의 범위를 지정하는 다른 방법입니다.

  • [:alnum:] 는 알파벳이나 숫자와 일치하고 [A-Za-z0-9] 와 같은 표현입니다.

  • [:alpha:] 는 알파벳과 일치하고 [A-Za-z] 와 같은 표현입니다.

  • [:blank:] 는 빈 칸이나 탭과 일치합니다.

  • [:cntrl:] 는 제어 문자들과 일치합니다.

  • [:digit:] 는 10진 숫자들과 일치하고 [0-9] 와 같은 표현입니다.

  • [:graph:] (출력가능한 그래픽 문자들). 아스키 33 - 126 의 문자들과 일치합니다. 빈 칸 문자가 포함되지 않는다는 것만 제외하고는 밑에서 설명할 [:print:] 와 같습니다.

  • [:lower:] 는 알파벳 소문자와 일치하고 [a-z] 와 같은 표현입니다.

  • [:print:] (출력 가능한 문자들). 아스키 32 - 126 까지의 문자들과 일치합니다. 위에서 설명한 [:graph:] 와 같지만 빈 칸 문자가 포함되어 있습니다.

  • [:space:] 는 공백문자들과 일치합니다(빈 칸, 수평탭).

  • [:upper:] 는 알파벳 대문자와 일치하고 [A-Z] 와 같은 표현입니다.

  • [:xdigit:] 는 16진수 숫자와 일치하고 [0-9A-Fa-f] 와 같은 표현입니다.

    중요: POSIX 문자 클래스는 보통 쿼우팅이나 이중 대괄호([[ ]])를 해 줘야 합니다.

    bash$ grep [[:digit:]] test.file
    abc=723
    	      

    이 문자 클래스들은 제한된 확장 형태로 globbing에서 쓰일 수도 있습니다.

    bash$ ls -l ?[[:digit:]][[:digit:]]?
    -rw-rw-r--    1 bozo  bozo         0 Aug 21 14:47 a33b
    	      

    스크립트에서 POSIX 문자 클래스가 쓰이는 예제는 예 12-14예 12-15 를 참고하세요.

Sed, awk, 은 스크립트에서 정규 표현식을 인자로 받아 파일이나 I/O 스크림을 걸러주거나 필터링 해줍니다. 이런 예제는 예 A-7예 A-12 를 참고하세요.

Dougherty와 Robbins가 쓴 "Sed & Awk"에서 정규 표현식에 대한 완전하고 명쾌한 사용법을 볼 수 있습니다(서지사항 참고).

주석

[1]

어떤 메타문자도 포함하지 않아서 문자열 자체의 뜻을 그대로 갖고 있는 문자열이 가장 간단한 형태의 정규 표현식입니다.

[2]

sed, awk, grep은 한 줄에 대해서 처리를 하기 때문에 뉴라인을 처리하지 못 합니다. 뉴라인이 들어 있는 여러 줄에 걸친 표현식에서 점(.)은 뉴라인과 일치합니다.

#!/bin/bash
sed -e 'N;s/.*/[&]/' << EOF   # Here Document
line1
line2
EOF
# 출력:
# [line1
# line2]
echo
awk '{ $0=$1 "\n" $2; if (/line.1/) {print}}' << EOF
line 1
line 2
EOF
# 출력:
# line
# 1
# Thanks, S.C.
exit 0


19.2. Globbing

Bash 자체는 정규 표현식을 이해하지 못 합니다. 스크립트에서는 sedawk 같은 명령어나 유틸리티가 정규 표현식을 해석해 줍니다.

Bash는 "globbing"이라고 하는 파일명 확장을 수행해 주는데 이는 표준 정규 표현식을 쓰지 않고, 대신에 와일드 카드를 인식하고 확장해 줍니다. globbing은 표준 와일드 카드 문자인 *, ?, 대괄호속의 문자 목록, 다른 특수 문자들(일치하지 않도록 부정해 주는 ^ 같은 문자)을 해석해 줍니다. 하지만 globbing시, 와일드 카드 문자 사용에는 중요한 몇가지 제한 사항이 있습니다. 예를 들어, *.bashrc처럼 점으로 시작하는 파일과 일치하지 않습니다. [1] 비슷하게, ?도 파일명 확장에서 쓰이면 정규 표현식의 일부분으로 쓰일 때와는 다른 의미를 갖습니다.

bash$ ls -l
total 2
-rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 a.1
-rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 b.1
-rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 c.1
-rw-rw-r--    1 bozo  bozo       466 Aug  6 17:48 t2.sh
-rw-rw-r--    1 bozo  bozo       758 Jul 30 09:02 test1.txt
bash$ ls -l t?.sh
-rw-rw-r--    1 bozo  bozo       466 Aug  6 17:48 t2.sh
bash$ ls -l [ab]*
-rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 a.1
-rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 b.1
bash$ ls -l [a-c]*
-rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 a.1
-rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 b.1
-rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 c.1
bash$ ls -l [^ab]*
-rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 c.1
-rw-rw-r--    1 bozo  bozo       466 Aug  6 17:48 t2.sh
-rw-rw-r--    1 bozo  bozo       758 Jul 30 09:02 test1.txt
bash$ ls -l {b*,c*,*est*}
-rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 b.1
-rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 c.1
-rw-rw-r--    1 bozo  bozo       758 Jul 30 09:02 test1.txt
bash$ echo *
a.1 b.1 c.1 t2.sh test1.txt
bash$ echo t*
t2.sh test1.txt
	      

echo 명령어도 파일이름에 대해서 와일드 카드 확장을 해 줍니다.

예 10-4를 참고하세요.

주석

[1]

파일명 확장은 직접적으로 점(dot)을 표시해줘야 도트파일(dotfile)과 일치합니다.

~/[.]bashrc    # ~/.bashrc 로 확장되지 않습니다.
~/?bashrc      # 이것도 안 됩니다.
               # globbing에서는 와일드 카드와 메타문자가 점으로 확장되지 않습니다.
~/.[b]ashrc    # ~./bashrc 로 확장됩니다.
~/.ba?hrc      # 역시 됩니다.
~/.bashr*      # 되겠죠?
# "dotglob" 옵션을 설정하면 이 기능을 꺼 줍니다.
# Thanks, S.C.