기본 정규표현식 (BRE)
정규표현식은 패턴을 표현하는 언어입니다. "숫자 3개가 연속되는 부분"을 찾고 싶을 때, if문으로 쓰면 코드가 길지만 정규표현식으로는 [0-9]\{3\} 한 줄이면 됩니다.
grep은 기본적으로 BRE(Basic Regular Expression, 기본 정규표현식)를 사용합니다. BRE는 POSIX 표준에 정의된 정규표현식의 기본 방언입니다. 먼저 BRE의 규칙을 익히고, 다음 절에서 더 편리한 ERE와 비교합니다.
정규표현식이란
일반 문자열 검색은 정확한 문자를 찾습니다. grep "error" log.txt는 딱 "error"라는 문자열만 찾습니다. 정규표현식은 패턴으로 검색합니다. grep "err[a-z]*" log.txt는 "err"로 시작하는 모든 단어를 찾습니다.
BRE 메타문자
메타문자는 자기 자신이 아닌 특별한 의미를 갖는 문자입니다.
| 메타문자 | 의미 | 예시 패턴 | 매칭 예 |
|---|---|---|---|
. |
임의의 한 문자 | c.t |
cat, cbt, c3t |
* |
앞 문자가 0번 이상 반복 | ab*c |
ac, abc, abbc |
^ |
줄의 시작 | ^root |
root로 시작하는 줄 |
$ |
줄의 끝 | bash$ |
bash로 끝나는 줄 |
[] |
문자 클래스 (하나 매칭) | [aeiou] |
a, e, i, o, u 중 하나 |
[^] |
부정 문자 클래스 | [^0-9] |
숫자가 아닌 문자 |
\ |
다음 문자를 이스케이프 | \. |
점 문자 자체 |
BRE에서 (, ), {, }, +, ?는 이스케이프(\)해야 특수 기능을 합니다. 이스케이프 없이 쓰면 일반 문자로 처리됩니다.
메타문자를 하나씩 배우기
실습 파일을 만들겠습니다.
# 새 파일: /tmp/sample.txtcat > /tmp/sample.txt << 'EOF'catcarcabbatcancartcardcarebarebarbar123errorerrorserrEOF
점(.) — 임의의 한 문자
# c와 t 사이에 임의의 한 문자grep "c.t" /tmp/sample.txt
cat
# c와 r 사이에 임의의 한 문자grep "c.r" /tmp/sample.txt
car
cart
card
care
별표(*) — 앞 문자 0번 이상 반복
주의할 점이 있습니다. *는 프로그래밍 언어의 "임의의 문자열"이 아닙니다. "앞 문자가 0번 이상"입니다.
# c, a가 0번 이상, r — "car", "cr", "caar", "caaar" 등grep "ca*r" /tmp/sample.txt
car
cart
card
care
bare # bar와 매칭 (a가 0번이면 cr이 되고... 아니다, bare는 b-a-r-e다)
bar # 이것도 아닌데...
잠깐, 예상과 다른 결과가 나왔습니다. ca*r은 "c 다음에 a가 0번 이상, 그다음에 r"입니다. "car"는 a가 1번이라 매칭됩니다. bare나 bar는 c가 없으므로 매칭되지 않습니다.
# 더 명확한 예: ab가 0번 이상grep "ab*c" /tmp/sample.txt
이 패턴에 매칭하는 단어가 없으니 결과가 없습니다. .*처럼 .과 조합해 "임의의 문자열"을 표현합니다.
# c로 시작하고 그 뒤에 무엇이든grep "c.*" /tmp/sample.txt
cat
car
cab
can
cart
card
care
앵커 — 줄의 시작과 끝
# c로 시작하는 줄grep "^c" /tmp/sample.txt
cat
car
cab
can
cart
card
care
# r로 끝나는 줄grep "r$" /tmp/sample.txt
car
bar
# 정확히 "cat"만 (c로 시작, a가 있고, t로 끝나고 줄이 끝남)grep "^cat$" /tmp/sample.txt
cat
문자 클래스 []
# a 또는 e가 있는 줄grep "[ae]" /tmp/sample.txt
cat
car
cab
can
cart
card
care
bare
bar
bar123
# 숫자가 있는 줄grep "[0-9]" /tmp/sample.txt
bar123
# 숫자가 없는 줄 (부정 클래스)grep "^[^0-9]*$" /tmp/sample.txt
cat
car
cab
bat
can
cart
card
care
bare
bar
error
errors
err
BRE의 이스케이프 메타문자
BRE에서 +, ?, |, (, ), {, }는 앞에 \를 붙여야 특수 기능을 합니다.
# BRE에서 "정확히 1번 이상" 반복: \+grep "er\+" /tmp/sample.txt
error
errors
err
# BRE에서 그룹화: \( \)grep "\(car\)" /tmp/sample.txt
car
cart
card
care
# BRE에서 반복 횟수: \{n\}grep "r\{2\}" /tmp/sample.txt
error
errors
이런 이스케이프가 불편하다면 ERE를 씁니다. 다음 절에서 배울 ERE는 이 메타문자들을 이스케이프 없이 씁니다.
grep에서 BRE 확인
# /etc/passwd에서 bash 쉘 사용자 찾기 (줄 끝이 /bin/bash)grep "/bin/bash$" /etc/passwd
root:x:0:0:root:/root:/bin/bash
jeongps:x:1000:1000::/home/jeongps:/bin/bash
# UID가 1000번 이상인 일반 사용자 찾기 (세 자리 이상 숫자)grep ":[0-9]\{4,\}:" /etc/passwd | head -3
# 빈 줄 찾기grep "^$" /tmp/sample.txt | wc -l
0
sample.txt에는 빈 줄이 없습니다.
BRE의 핵심은 이것입니다. .(임의의 문자), *(0번 이상), ^(줄 시작), $(줄 끝), [](문자 클래스). 이 다섯 가지만 익혀도 대부분의 패턴을 쓸 수 있습니다.