Journey to Security/리눅스

sed에서 사용하는 정규표현식 - BRE와 ERE

Cordilog 2026. 6. 18. 19:10

1. sed와 정규표현식

sed(Stream Editor)는 텍스트 스트림을 한 줄씩 읽어 패턴 매칭과 변환을 수행하는 도구다.

이때 패턴을 기술하는 언어가 바로 정규표현식(Regular Expression)이다.

sed는 기본적으로 BRE(Basic Regular Expression)를 사용하며, -E 또는 -r 옵션을 주면 ERE(Extended Regular Expression)로 전환된다.

# BRE (기본)
sed 's/pattern/replacement/' file

# ERE (확장)
sed -E 's/pattern/replacement/' file

BRE와 ERE의 가장 큰 차이는 메타문자에 백슬래시가 들어가는지 여부에 있다.

기능은 같지만 가독성에서 차이가 있을 수 있다.

 

2. BRE vs ERE 

기능 BRE ERE(-E) 설명
그룹핑 \( \) ( ) 캡처 그룹
반복 1회 이상 \+ + 1회 이상 반복
반복 0 또는 1회 \? ? 선택적 매칭
OR | | 택일
반복 횟수 지정 \{ \} { } 정확한 반복 횟수
. * ^ $ [ ] 동일 동일 양쪽 모두 메타문자

📌BRE에서는 ( ) + ? { } |가 일반 문자이고, 메타문자로 쓰려면 \를 붙인다. ERE에서는 그 반대다.

# 같은 의미의 표현
# BRE: 숫자 1회 이상 반복
echo "port 8080" | sed 's/[0-9]\+/***/'

# ERE: 같은 동작이지만 가독성이 더 좋다
echo "port 8080" | sed -E 's/[0-9]+/***/'

 

3. 메타문자

sed에서 사용 가능한 정규표현식 메타문자를 기능별로 분류한다.

1️⃣ 위치 지정 (Anchor)

위치 앵커는 문자열 내의 위치를 지정하며, 문자 자체를 매칭하지 않는다.

메타문자 의미 예시
^ 줄의 시작 ^root → root로 시작하는 줄
$ 줄의 끝 error$ → error로 끝나는 줄
# 주석 줄(#으로 시작) 삭제
sed '/^#/d' /etc/sysctl.conf

# 빈 줄 삭제
sed '/^$/d' config.txt

# 줄 끝 공백 제거
sed 's/[[:space:]]*$//' file.txt

^$제로 폭(zero-width) 매칭이라는 점이 중요하다. 즉, 문자를 소비하지 않고 위치만 확인한다.

 

2️⃣ 문자 매칭

메타문자 의미 예시
. 임의의 한 문자 (개행 제외) h.t → hat, hit, hot 등
[abc] 문자 클래스: a, b, c 중 하나 [Yy]es → Yes 또는 yes
[^abc] 부정 문자 클래스: a, b, c 제외 [^0-9] → 숫자가 아닌 문자
[a-z] 범위: a부터 z까지 [A-Za-z] → 알파벳
# IP 주소 패턴 매칭 (대략적)
sed -n '/[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/p' access.log

# 대소문자 무관하게 error 찾기
sed -n '/[Ee][Rr][Rr][Oo][Rr]/p' syslog

3️⃣ POSIX 문자 클래스

[a-z] 같은 범위 표현은 로케일(locale)에 따라 다르게 동작할 수 있다.

POSIX 문자 클래스는 로케일 독립적이어서 더 안전하다.

클래스 의미 등가 표현
[:alpha:] 알파벳 [A-Za-z]
[:digit:] 숫자 [0-9]
[:alnum:] 알파벳 + 숫자 [A-Za-z0-9]
[:upper:] 대문자 [A-Z]
[:lower:] 소문자 [a-z]
[:space:] 공백 문자 스페이스, 탭, 개행 등
[:blank:] 수평 공백 스페이스, 탭
[:punct:] 구두점  
[:print:] 출력 가능 문자  
# POSIX 클래스는 대괄호 안에서 사용 (이중 대괄호)
sed 's/[[:digit:]]/#/g' file.txt
#         ^     ^
#    외부 []는 문자 클래스, 내부 [::]는 POSIX 클래스

[[:digit:]]에서 대괄호가 이중으로 들어가는 이유: 바깥 [ ]는 정규표현식의 문자 클래스 구문이고, 안쪽 [:digit:]는 POSIX 클래스 이름이다.

 

4️⃣ 수량자 (Quantifier)

메타문자 BRE ERE 의미
* * * 0회 이상 반복
\+ / + \+ + 1회 이상 반복
\? / ? \? ? 0 또는 1회
\{n\} / {n} \{n\} {n} 정확히 n회
\{n,\} / {n,} \{n,\} {n,} n회 이상
\{n,m\} / {n,m} \{n,m\} {n,m} n회 이상 m회 이하
# BRE: 숫자 3~5자리 매칭
echo "port 8080" | sed 's/[0-9]\{3,5\}/[PORT]/'

# ERE: 같은 표현
echo "port 8080" | sed -E 's/[0-9]{3,5}/[PORT]/'

# 연속 공백을 하나로 축소
sed -E 's/[[:space:]]+/ /g' messy.txt 

4. 그룹핑과 역참조

1️⃣ 캡처 그룹

괄호로 묶은 패턴을 캡처 그룹이라 하며, 매칭된 내용을 \1, \2 등으로 역참조할 수 있다.

# BRE: 괄호에 백슬래시 필요
echo "2025-06-18" | sed 's/\([0-9]\{4\}\)-\([0-9]\{2\}\)-\([0-9]\{2\}\)/\3\/\2\/\1/'
# 결과: 18/06/2025

# ERE: 깔끔한 문법
echo "2025-06-18" | sed -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3\/\2\/\1/'
# 결과: 18/06/2025

2️⃣ 전체 매칭 참조: &

&는 패턴 전체에 매칭된 문자열을 나타낸다. 캡처 그룹 없이도 매칭 결과를 재활용할 수 있다.

# 모든 숫자를 대괄호로 감싸기
echo "Error on line 42" | sed 's/[0-9]\+/[&]/g'
# 결과: Error on line [42]

# 모든 단어 앞에 > 붙이기
echo "hello world" | sed 's/[[:alpha:]]\+/> &/g'
# 결과: > hello > world

&는 치환부에서만 의미를 가진다. 검색부에서는 일반 문자 &로 취급된다.

 

3️⃣ 역참조 실전 활용

# 연속 중복 단어 찾기 ("the the" 같은 오타)
sed -E -n '/\b([a-zA-Z]+) \1\b/p' document.txt

# 첫 번째와 두 번째 필드 swap (콜론 구분)
echo "root:x:0:0" | sed -E 's/^([^:]+):([^:]+)/\2:\1/'
# 결과: x:root:0:0

# 따옴표 종류 변환 (작은 → 큰)
sed -E "s/'([^']*)'/\"\1\"/g" script.sh
 

5. 정규표현식으로 주소 지정하기

sed 명령에서 정규표현식은 처리할 줄을 선택하는 주소(address)로도 사용된다.

1️⃣ 기본 주소 패턴

# 정규표현식 매칭되는 줄에만 명령 적용
sed '/pattern/command'

# 줄 번호와 조합
sed '1,/^END/d'        # 1번 줄부터 END로 시작하는 줄까지 삭제

# 두 정규표현식 사이의 범위
sed '/^START/,/^END/d'  # START부터 END까지 삭제

2️⃣ 주소 부정 (!)

# 패턴이 매칭되지 않는 줄에 명령 적용
sed '/^#/!s/foo/bar/g'  # 주석이 아닌 줄에서만 foo→bar 치환

# 빈 줄이 아닌 줄만 출력
sed -n '/^$/!p' file.txt
 
6. 치환 명령(s)의 플래그

 

s/pattern/replacement/flags 구조에서 치환 동작을 제어하는 플래그를 정리한다.

플래그 의미
g 줄 내 모든 매칭을 치환 (없으면 첫 번째만)
p 치환이 발생한 줄을 출력
w file 치환이 발생한 줄을 파일에 기록
i (GNU) 대소문자 무시
n n번째 매칭만 치환
# 기본: 줄의 첫 번째 매칭만 치환
echo "aaa bbb aaa" | sed 's/aaa/xxx/'
# 결과: xxx bbb aaa

# g: 모든 매칭 치환
echo "aaa bbb aaa" | sed 's/aaa/xxx/g'
# 결과: xxx bbb xxx

# 2: 두 번째 매칭만 치환
echo "aaa bbb aaa" | sed 's/aaa/xxx/2'
# 결과: aaa bbb xxx

# i: 대소문자 무시
echo "Hello HELLO hello" | sed 's/hello/Hi/gi'
# 결과: Hi Hi Hi

# -n + p 조합: 치환된 줄만 출력
sed -n 's/error/ERROR/p' syslog

7. 패턴 실습

1️⃣ 로그 파일 처리

# IP 주소 마스킹
sed -E 's/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/[MASKED]/g' access.log

# 타임스탬프 형식 변환 (YYYY-MM-DD → DD/MM/YYYY)
sed -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3\/\2\/\1/g' log.txt

# 특정 로그 레벨만 추출
sed -n '/\[ERROR\]/p' application.log
sed -n '/\[WARN\]\|[ERROR\]/p' application.log

2️⃣ 설정 파일 편집

# key=value에서 특정 키의 값 변경
sed -E 's/^(PORT=).*/\18080/' .env

# 주석 처리 / 주석 해제
sed 's/^/#/' config.conf           # 전체 주석 처리
sed 's/^#//' config.conf           # 주석 해제
sed '/^#.*OPTION/s/^#//' config    # 특정 옵션만 주석 해제

# 빈 줄과 주석 줄 동시 제거
sed -E '/^(#|$)/d' config.conf

3️⃣ 텍스트 정제

# HTML 태그 제거
sed -E 's/<[^>]+>//g' page.html

# 연속 빈 줄을 하나로 축소
sed '/^$/N;/^\n$/d' file.txt

# 줄 앞뒤 공백 제거 (trim)
sed -E 's/^[[:space:]]+//;s/[[:space:]]+$//' file.txt

# CSV에서 특정 필드 추출 (2번째 필드)
sed -E 's/^[^,]*,([^,]*).*/\1/' data.csv

8. 정리

구분 BRE(기본) ERE(-E)
사용 권장 간단한 패턴, 레거시 스크립트 그룹핑·수량자가 많은 복잡한 패턴
그룹/수량자 \( \) \+ \? \{ \} ( ) + ? { }
가독성 백슬래시로 인해 가독성 떨어짐 깔끔한 문법
호환성 POSIX 표준, 모든 sed GNU/BSD sed 지원