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
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 지원 |
'Journey to Security > 리눅스' 카테고리의 다른 글
| awk - 필드 단위 텍스트 처리 도구 (0) | 2026.06.18 |
|---|---|
| 스트림 편집기 sed 명령/옵션 정리 (0) | 2026.06.18 |
| 우분투 서버에 Claude Code 설치하기 (0) | 2026.06.17 |
| [방화벽] iptables LOG target- 패킷 모니터링 (0) | 2026.06.03 |
| [방화벽] iptables Conntrack (2) INVALID 패킷 탐지 (0) | 2026.06.03 |