thenullpage.com
인터넷에서 잘 쓰겠다 싶은 정규식 한 줄을 복사해 터미널에 붙여 넣었는데 결과가 하나도 안 나온 적 있으신가요. 오타도 아니고 파일에 그 내용이 분명히 있는데도요. 저는 한참 동안 이걸 제 실수라고만 생각했어요. 알고 보니 정규식은 하나의 언어가 아니라 도구마다 조금씩 다른 방언을 쓰고 있었어요. 오늘은 같은 패턴이 왜 여기서는 되고 저기서는 안 되는지, 그 경계선을 직접 돌려보면서 정리해볼게요.
13.1 왜 grep에서만 결과가 안 나오죠?
이런 파일이 있다고 해볼게요.
$ cat log.txt
error 12
warning 3
error 456
ok
숫자가 붙은 에러 줄만 뽑아볼게요.
$ grep "error [0-9]+" log.txt
결과: (아무것도 안 나옴)
패턴은 멀쩡해 보이는데 빈손이에요. 이유는 기본 grep이 쓰는 문법이 우리가 아는 정규식보다 오래된 것이라서예요. 이걸 기본 정규식(BRE, Basic Regular Expression)이라고 불러요. 여기서 +는 "한 번 이상 반복"이 아니라 그냥 더하기 기호 글자예요. 그러니까 위 명령은 "error 숫자 다음에 더하기 기호"를 찾은 거고, 그런 줄이 없으니 아무것도 안 나온 거예요.
반복이라는 뜻으로 쓰려면 역슬래시를 붙여야 해요.
$ grep "error [0-9]\+" log.txt
결과:
error 12
error 456
거꾸로죠. 보통은 특수한 뜻을 없앨 때 역슬래시를 붙이는데, BRE에서는 특수한 뜻을 살릴 때 붙여요. +만 그런 게 아니라 ?와 |, 괄호와 중괄호까지 전부 이 대접을 받아요.
$ grep "error\|warning" log.txt
결과:
error 12
warning 3
error 456
13.2 그럼 -E만 붙이면 해결인가요?
거의 그래요. -E를 붙이면 확장 정규식(ERE)으로 바뀌어서 익숙한 모양 그대로 쓸 수 있어요.
$ grep -E "error [0-9]+" log.txt
결과:
error 12
error 456
그런데 여기서 두 번째 함정이 나와요.
$ grep -E "error \d+" log.txt
결과: (아무것도 안 나옴)
\d가 안 통해요. \d와 \w와 \s는 POSIX 계열 문법에 원래 없는 표기거든요. 펄에서 시작해 파이썬과 자바스크립트로 퍼진 축약형이에요. grep의 ERE는 이걸 숫자로 안 보고 그냥 글자 d로 읽어요.
대신 이름이 붙은 문자 클래스가 있어요.
$ grep -E "error [[:digit:]]+" log.txt
결과:
error 12
error 456
대괄호가 두 겹인 게 어색하죠. [[:digit:]]가 숫자, [[:alpha:]]가 글자, [[:space:]]가 공백이에요. 사실 그냥 [0-9]를 쓰는 게 짧고 어디서나 통해요.
리눅스에는 -P라는 탈출구도 있어요. 펄 문법을 그대로 쓰는 모드예요.
$ grep -P "error \d+" log.txt
결과:
error 12
error 456
다만 -P는 맥의 기본 grep에는 없어요. 스크립트에 넣어두면 내 컴퓨터에서만 돌아가는 코드가 되기 쉬워요.
13.3 sed도 같은 함정이 있나요?
더 심해요. 날짜 순서를 뒤집는 치환을 익숙한 문법으로 써볼게요.
$ echo "2026-08-01" | sed "s/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3.\2.\1/"
결과: sed: invalid reference \3 on `s' command's RHS
괄호를 그룹으로 안 봤으니 3번 그룹 같은 건 없다고 화를 낸 거예요. BRE 방식으로 쓰면 이렇게 돼요.
$ echo "2026-08-01" | sed "s/\([0-9]\{4\}\)-\([0-9]\{2\}\)-\([0-9]\{2\}\)/\3.\2.\1/"
결과: 01.08.2026
역슬래시가 너무 많아서 읽기가 괴롭죠. sed도 -E를 받아요.
$ echo "2026-08-01" | sed -E "s/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3.\2.\1/"
결과: 01.08.2026
여기서 놓치기 쉬운 게 하나 있어요. -E로 바뀌는 건 찾는 쪽 문법뿐이고, 바꿔 넣는 쪽에서 그룹을 부르는 표기는 그대로 \1이에요. 자바스크립트 습관대로 달러 기호를 쓰면 이렇게 돼요.
$ echo "2026-08-01" | sed -E "s/([0-9]{4})-([0-9]{2})-([0-9]{2})/$3.$2.$1/"
결과: $3.$2.$1
에러도 안 나고 글자 그대로 박혀버려요. 조용히 틀리는 쪽이 더 무서운 종류의 실수예요.
13.4 파이썬과 자바스크립트는 뭐가 다른가요?
둘 다 펄 계열이라 패턴 문법은 거의 같아요. 갈리는 건 주변부예요. 먼저 이름 붙인 그룹이에요.
파이썬: (?P<y>\d{4})
자바스크립트: (?<y>\d{4})
파이썬만 P가 하나 더 붙어요. 반대로 자바스크립트에 P를 넣으면 문법 오류가 나요.
치환에서 그룹을 부르는 표기도 달라요.
re.sub(r"(\d{4})-(\d{2})-(\d{2})", r"\3.\2.\1", "2026-08-01")
결과: '01.08.2026'
"2026-08-01".replace(/(\d{4})-(\d{2})-(\d{2})/, "$3.$2.$1")
결과: '01.08.2026'
파이썬은 \1, 자바스크립트는 $1이에요. 방금 본 sed는 -E를 써도 \1이었고요. 이 셋을 오가다 보면 제일 자주 틀리는 자리예요.
기본 동작에서도 한 번 놀라게 돼요.
re.sub(r"a", "X", "banana")
결과: 'bXnXnX'
"banana".replace(/a/, "X")
결과: 'bXnana'
파이썬 re.sub는 기본이 전부 바꾸기인데 자바스크립트 replace는 처음 하나만 바꿔요. 전부 바꾸려면 /a/g처럼 g 플래그를 붙이거나 replaceAll을 써야 해요. 치환 스크립트를 파이썬에서 자바스크립트로 옮길 때 소리 없이 결과가 달라지는 지점이에요.
13.5 문자열 끝을 뜻하는 $도 다른가요?
네, 이건 알아두면 언젠가 한 번은 구해줘요.
re.search(r"^\w+$", "hello\n")
결과: <re.Match object; span=(0, 5), match='hello'>
끝에 줄바꿈이 붙어 있는데도 통과했어요. 파이썬의 $는 문자열 맨 끝 줄바꿈 바로 앞도 끝으로 쳐주거든요. 자바스크립트는 안 그래요.
/^\w+$/.test("hello\n")
결과: false
파일을 한 줄씩 읽으면 끝에 줄바꿈이 딸려 오는 경우가 많아요. 그래서 똑같은 검증 패턴인데 파이썬 쪽만 통과하는 상황이 생겨요. 줄바꿈까지 막고 싶으면 파이썬에서 \Z를 쓰면 돼요.
re.search(r"^\w+\Z", "hello\n")
결과: None
13.6 아예 못 쓰는 기능도 있나요?
있어요. 고 언어의 기본 정규식과 러스트의 regex 라이브러리에는 룩어라운드와 역참조가 없어요. 아직 안 만든 게 아니라 일부러 뺀 것이에요. 이 둘을 포기하는 대신 입력이 길어져도 시간이 비례해서만 늘어나는 걸 보장하거든요.
그래서 (?<=USD )\d+ 같은 패턴을 그대로 가져가면 실행도 못 해보고 거부당해요. 없는 문법이라 우회할 방법이 없으니, 패턴을 다시 설계하거나 정규식으로는 넉넉히 뽑고 나머지는 일반 코드로 걸러내는 쪽이 맞아요.
정리하면 챙길 건 몇 개 안 돼요. 터미널에서는 +와 ?와 |부터 의심하고 되도록 -E를 붙이기, \d 대신 [0-9] 쓰기, 치환 표기는 sed와 파이썬이 \1이고 자바스크립트가 $1이라는 것. 그리고 정규식을 어디선가 가져올 때는 그게 어느 도구용으로 쓰인 건지 먼저 보세요. 오늘 것 중에 grep "error [0-9]+"와 grep -E "error [0-9]+" 두 줄만 직접 쳐보셔도 결과가 갈리는 순간이 손에 남아서, 다음부터는 저절로 -E에 손이 가게 돼요.