^ 와 $ 는 글자를 가리키지 않고 위치를 가리킨다. 폭이 없는 앵커다.
| 패턴 | 뜻 |
|---|---|
^ |
줄(또는 문자열)의 시작 위치 |
$ |
줄(또는 문자열)의 끝 위치 |
^$ |
시작 바로 다음이 끝. 즉 빈 줄 |
^abc |
abc 로 시작하는 줄 |
abc$ |
abc 로 끝나는 줄 |
^abc$ |
내용이 정확히 abc 인 줄 |
^$ 는 설정 파일에서 빈 줄을 거를 때 가장 많이 쓴다.
grep -v '^$' app.conf # 빈 줄 제거
grep -vE '^$|^#' app.conf # 빈 줄과 주석 제거
grep -vE '^[[:space:]]*($|#)' app.conf # 공백만 있는 줄과 들여쓴 주석까지
세 번째가 실전에서 맞다. 앞에 공백이 있는 줄은 ^$ 에 걸리지 않기 때문이다.
여러 줄 문자열에 적용할 때 ^·$ 가 줄 단위인지 문자열 단위인지가 엔진과 옵션에 따라 다르다.
import re
text = "a\nb\n"
re.findall(r"^b$", text) # []
re.findall(r"^b$", text, re.M) # ['b']
파이썬에서 $ 는 문자열 맨 끝의 개행 앞에서도 일치한다. 정말로 끝만 보려면 \Z 를 쓴다.
re.search(r"abc\Z", "abc\n") # None
re.search(r"abc$", "abc\n") # 일치한다
| 패턴 | 뜻 |
|---|---|
. |
개행을 뺀 임의의 한 글자 |
[abc] |
a 또는 b 또는 c |
[^abc] |
a·b·c 가 아닌 한 글자 |
[0-9] · \d |
숫자 |
\w |
단어 문자. 대개 [A-Za-z0-9_] |
\s |
공백류 |
* + ? |
0회 이상 · 1회 이상 · 0 또는 1회 |
{n,m} |
n 회 이상 m 회 이하 |
*? +? |
최소 일치(lazy) |
기본 수량자는 탐욕적이라 가능한 한 길게 먹는다. 태그나 따옴표 안을 잡을 때 특히 문제가 된다.
<a>x</a><b>y</b>
<.*> → <a>x</a><b>y</b> 전체를 먹는다
<.*?> → <a> 최소로 먹는다
<[^>]*> → <a> 더 명확하고 빠르다
같은 정규표현식이 어디서나 통하지 않는다.
| 도구 | 기본 문법 | 확장 |
|---|---|---|
grep |
POSIX BRE. + ? | () 앞에 백슬래시가 필요하다 |
grep -E 로 ERE, grep -P 로 PCRE |
sed |
BRE | sed -E |
awk |
ERE | — |
Python re |
자체 문법. PCRE 와 비슷하다 | — |
| Java | 자체 문법 | — |
\d 는 POSIX BRE·ERE 에 없다. grep -E '\d' 는 기대대로 동작하지 않으므로 [0-9] 또는 [[:digit:]] 를 쓴다. grep -P 를 쓸 수 있으면 \d 가 통하지만, 배포판에 따라 빌드에서 빠져 있다.
echo "2026-01-13" | sed -E 's/^([0-9]{4})-([0-9]{2})-([0-9]{2})$/\3\/\2\/\1/'
# 13/01/2026
import re
re.sub(r"(\d{4})-(\d{2})-(\d{2})", r"\3/\2/\1", "2026-01-13")
이름 붙인 그룹을 쓰면 나중에 읽기 쉽다.
m = re.match(r"(?P<host>[^:]+):(?P<port>\d+)", "db01:5432")
m.group("port")
한 번에 맞히려 하지 말고 조금씩 늘린다.
grep -nE '패턴' sample.log | head
echo "테스트 문자열" | grep -oE '패턴'
-o 는 일치한 부분만 출력해 패턴이 어디까지 먹었는지 바로 보여 준다.
파이썬에서는 re.VERBOSE 로 주석을 달아 둔다. 긴 패턴은 나중에 읽지 못한다.
pat = re.compile(r"""
^(?P<ip>\d{1,3}(?:\.\d{1,3}){3}) # 클라이언트 IP
\s+-\s+-\s+
\[(?P<ts>[^\]]+)\] # 시각
""", re.VERBOSE)