sed(stream editor)는 파일이나 표준 입력을 한 줄씩 읽어 “패턴 공간(pattern space)“이라는 임시 버퍼에 넣고, 지정한 스크립트(치환·삭제·출력 명령)를 그 버퍼에 적용한 뒤 결과를 흘려보내는 비대화형 스트림 편집기다. vi나 nano처럼 화면을 띄우고 커서를 옮기며 고치는 대신, 스크립트 한 줄로 파일 수천 개를 무인 배치로 일괄 변환할 수 있다.
이 장을 읽기 전에
선행 챕터: 이 장은 13장: grep에서 패턴에 맞는 줄을 찾는 법을 다룬 뒤 이어진다. grep이 패턴을 찾기만 했다면, sed는 찾은 패턴을 그 자리에서 바꾼다 — “찾기"에서 “찾아서 바꾸기"로 넘어가는 자연스러운 전환이다. 정규식 자체(BRE/ERE 메타문자, 캡처 그룹)에 대한 기초 감각이 있으면 예시를 읽기 더 수월하다.
이 장의 깊이: 입문–중급 난이도다. 기본 치환·삭제·출력부터 여러 명령 조합, 주소 범위 지정, 백참조까지 실무에서 자주 쓰는 범위를 다룬다. 다루지 않는 것: 줄 단위를 넘어 필드(컬럼) 단위로 데이터를 다루는 처리는 15장: awk에서 다룬다. sed의 홀드 공간(hold space)을 활용한 다줄 스크립트, 라벨(:label)과 분기(b, t)를 쓰는 고급 스크립팅은 이 장의 범위 밖이며 GNU sed 매뉴얼의 심화 절을 참고해야 한다.
당신의 수준에 맞는 경로
| 수준 | 읽을 부분 | 핵심 목표 |
|---|---|---|
| 텍스트를 빨리 치환·삭제해야 하는 사람 | “개요 + 정신 모델”, “사용법 · 옵션”, “예시"의 “기본 치환”·“삭제·출력” | s///와 d, -n+p 조합으로 파일 내용을 즉시 바꾸거나 원하는 줄만 뽑아낼 수 있다 |
| 스크립트에 sed를 안전하게 넣으려는 사람 | “예시"의 “여러 명령 조합”·“주소 범위”, “주의사항·함정” 전체 | 여러 파일을 자동으로 편집하는 스크립트에서 GNU/BSD 이식성 문제 없이 -i를 안전하게 쓸 수 있다 |
개요 + 정신 모델
sed를 이해하는 가장 빠른 방법은 “이 도구가 파일을 어떻게 본다"는 관점을 바꾸는 것이다. sed는 파일 전체를 메모리에 올려놓고 편집하는 워드프로세서가 아니라, 입력을 줄 단위로 순회하는 파이프라인 필터다. 각 줄을 읽을 때마다 패턴 공간에 넣고, 스크립트에 적힌 명령을 순서대로 적용한 뒤, 명시적으로 -n을 주지 않았다면 그 결과를 자동으로 표준 출력에 흘려보내고 다음 줄로 넘어간다. 이 사이클이 파일 끝까지 반복된다.
이 모델에서 세 가지가 자연스럽게 따라나온다. 첫째, sed는 기본적으로 한 줄 앞뒤를 동시에 보지 못한다 — 각 줄이 독립적으로 패턴 공간에 들어왔다 나가기 때문에, 여러 줄에 걸친 패턴을 다루려면 홀드 공간 같은 별도 메커니즘이 필요하다(이 장의 범위 밖). 둘째, sed는 원본을 직접 수정하지 않는다 — 표준 출력으로 결과를 흘려보내는 것이 기본 동작이며, “파일을 직접 고치는 것처럼 보이는” -i 옵션조차 실제로는 임시 파일에 새 내용을 쓰고 끝에 원본과 바꿔치기하는 방식으로 동작한다(자세한 내용은 아래 흔한 오개념 참고). 셋째, grep이 “매칭되는 줄을 찾아 그대로 보여주는” 도구였다면, sed는 그 매칭 지점에서 치환·삭제·삽입 같은 변형을 가한 뒤 흘려보낸다는 점에서 grep의 자연스러운 다음 단계다.
사용법 · 옵션
| |
- 스크립트: 작은따옴표로 감싼 편집 명령. 파일을 생략하면 표준 입력에서 읽는다.
- 명령을 여러 개 적용하려면
-e를 반복하거나, 스크립트 안에서 세미콜론(;)으로 구분한다.
명령 (스크립트 안에서 쓰는 편집 동작)
| 명령 | 설명 |
|---|---|
s/패턴/대체/플래그 | 치환 — 가장 많이 쓰는 명령 |
d | 패턴 공간(해당 줄) 삭제 — 자동 출력도 생략됨 |
p | 패턴 공간 출력 — 보통 -n과 함께 써서 원하는 줄만 뽑는다 |
q | 지금까지 처리한 결과를 출력하고 즉시 종료 |
a\텍스트 | 해당 줄 다음에 텍스트 추가 |
i\텍스트 | 해당 줄 앞에 텍스트 삽입 |
c\텍스트 | 해당 줄(또는 범위)을 텍스트로 치환 |
y/집합1/집합2/ | 문자 단위 변환(tr과 유사, 정규식 아님) |
옵션
| 옵션 | 설명 |
|---|---|
-n, --quiet | 패턴 공간을 자동 출력하지 않음 (p 명령과 조합해 원하는 줄만 출력) |
-e SCRIPT | 스크립트를 추가로 지정(여러 개 조합 가능) |
-f FILE | 스크립트를 파일에서 읽음 |
-i[SUFFIX] | 파일을 직접 수정(in-place). GNU와 BSD/macOS의 동작 차이가 커서 아래 주의사항에서 별도로 다룬다 |
-E, -r, --regexp-extended | 확장 정규식(ERE) 사용. 기본값은 기본 정규식(BRE) |
-s, --separate | 여러 파일을 각각 독립된 스트림으로 취급(기본은 하나로 이어붙임) |
치환 플래그 (s/// 뒤에 붙는 옵션)
| 플래그 | 설명 |
|---|---|
g | 줄 안의 모든 매칭을 치환(기본은 줄당 첫 번째만) |
N | 줄 안의 N번째 매칭만 치환 |
p | 치환이 일어났을 때만 결과 출력(-n과 함께 자주 씀) |
i, I | 대소문자 구분 없이 매칭 |
w FILE | 치환된 줄을 FILE에 저장 |
주소 지정 (어느 줄에 명령을 적용할지)
| 형식 | 설명 |
|---|---|
N | N번째 줄 |
N,M | N번째 줄부터 M번째 줄까지 |
/정규식/ | 정규식과 일치하는 줄 |
N,/정규식/ | N번째 줄부터 정규식이 처음 일치하는 줄까지 |
N~M | N번째 줄부터 M줄 간격으로(GNU 확장, BSD/POSIX sed에는 없음) |
$ | 마지막 줄 |
! | 주소 뒤에 붙이면 조건 반전(해당하지 않는 줄에 적용) |
예시
기본 치환
| |
삭제 · 출력
| |
여러 명령 조합
| |
주소 범위·조건 반전
| |
캡처 그룹과 백참조
| |
슬래시가 많은 경로 치환 (구분자 변경)
| |
파일 직접 수정 (in-place)
| |
주의사항 · 함정
GNU sed -i와 BSD/macOS sed -i는 접미사 인자 요구 방식이 다르다 — 몰랐다가 macOS에서 파일이 깨지는 사고가 매우 흔하다. GNU sed 매뉴얼은 -i 옵션을 -i[SUFFIX], --in-place[=SUFFIX]로 명시한다. 대괄호가 뜻하는 대로 SUFFIX는 완전히 생략 가능한 선택 인자이고, 매뉴얼 본문도 “If no extension is supplied, the original file is overwritten without making a backup”(접미사를 주지 않으면 백업 없이 원본을 덮어쓴다)라고 밝힌다. 즉 sed -i 's/a/b/' file.txt처럼 접미사 없이 -i만 써도 GNU sed는 정상적으로 원본을 즉시 수정한다.
반면 BSD 계열(macOS 기본 sed 포함) man 페이지는 SYNOPSIS에서 -i extension을 대괄호 없이 표기한다 — -i를 쓰는 순간 그 다음 토큰을 반드시 접미사로 소비한다는 뜻이다. FreeBSD sed(1) man 페이지는 “-i extension … Edit files in-place … If a zero-length extension is given, no backup will be saved"라고 설명하는데, 여기서 “zero-length extension을 줄 수 있다"는 말은 접미사 인자를 아예 생략해도 된다는 뜻이 아니라 빈 문자열('')을 명시적으로 인자 자리에 채워 넣어야 한다는 뜻이다. 이 차이를 모른 채 macOS에서 sed -i 's/a/b/' file.txt를 실행하면, BSD sed는 s/a/b/를 접미사 인자로 오인하고 file.txt를 편집 스크립트로 삼으려 시도한다 — 스크립트도 없이 실행되니 오류가 나거나, 최악의 경우 의도와 다른 파일이 백업 접미사 s/a/b/가 붙은 이름으로 만들어지고 원본은 스크립트로 잘못 해석되며 꼬인다. 크로스 플랫폼 스크립트를 짤 때는 sed -i.bak '...'처럼 접미사를 항상 붙이거나(백업 파일이 남는다), OS를 감지해 GNU면 -i, BSD/macOS면 -i ''를 분기하는 방식이 안전하다. macOS에 GNU sed를 그대로 쓰고 싶다면 Homebrew로 brew install gnu-sed를 설치해 gsed로 실행하는 방법도 있다.
구분자를 / 대신 다른 문자로 바꾸면 경로 치환이 훨씬 읽기 쉬워진다. s///의 구분자는 /로 고정된 것이 아니라 임의의 문자로 바꿀 수 있다. 파일 경로처럼 /가 반복되는 문자열을 치환할 때 기본 구분자를 그대로 쓰면 s/\/usr\/local\/bin/\/opt\/bin/g처럼 슬래시마다 백슬래시 이스케이프가 필요해 가독성이 급격히 떨어진다. 이럴 때는 s|/usr/local/bin|/opt/bin|g처럼 |나 # 등 패턴 안에 나오지 않을 문자를 구분자로 쓰는 관용구가 표준이다.
BRE와 ERE의 메타문자 이스케이프 규칙이 다르다. sed는 기본적으로 기본 정규식(BRE)을 쓰므로 +, ?, |, (, )는 리터럴 문자로 취급되고, 메타문자로 쓰려면 \+처럼 백슬래시를 붙여야 한다. -E(ERE)를 쓰면 이 이스케이프가 필요 없어지지만, BRE 스크립트와 ERE 스크립트를 섞어 복사·붙여넣기 하다 이스케이프를 빠뜨리거나 반대로 불필요하게 남겨 매칭이 조용히 실패하는 실수가 흔하다.
-i는 원본을 영구적으로 덮어쓴다. 백업 접미사 없이 -i(GNU) 또는 -i ''(BSD)를 실행하면 원본 파일은 즉시 사라지고 복구할 수 없다. 여러 파일에 일괄 적용하기 전에는 반드시 -i 없이 먼저 표준 출력으로 결과를 확인하거나, 백업 접미사를 붙여 실행하는 습관을 들여야 한다.
흔한 오개념
"-i는 파일을 그 자리에서 직접 고친다"는 표현이 실제 구현을 오해하게 만든다. GNU sed 매뉴얼은 -i가 “creating a temporary file and sending output to this file rather than to the standard output”(임시 파일을 만들고 표준 출력 대신 그 파일에 결과를 보낸다)이라고 명시한다. 처리가 끝나면 이 임시 파일이 원래 파일 이름으로 rename된다. 즉 -i도 내부적으로는 “새 파일을 만들고 이름을 바꿔치기"하는 것이지, 원본 파일의 바이트를 그 자리에서 고쳐 쓰는 것이 아니다. 이 사실은 디스크 공간이 부족한 상황에서 -i 편집이 중간에 실패하면 원본이 아예 사라질 수 있다는 실무 위험과도 연결된다.
치환 명령의 &가 리터럴 앰퍼샌드로 출력될 거라 기대하는 실수도 흔하다. s///의 대체 문자열 안에서 &는 매칭된 전체 문자열을 가리키는 특수 기호다(sed 's/[0-9]\+/[&]/g'처럼 매칭 부분을 괄호로 감싸는 관용구가 여기서 나온다). 대체 문자열에 리터럴 & 글자 자체를 넣고 싶다면 \&로 이스케이프해야 한다.
다음 장에서는
다음은 15장: awk — sed가 줄 단위로 치환·삭제했다면, awk는 줄을 필드(컬럼) 단위로 쪼개 집계·리포트를 만드는 한 단계 더 나아간 텍스트 처리 도구를 다룬다.
평가 기준
- sed가 각 줄을 패턴 공간에 읽어 스크립트를 적용한 뒤 흘려보내는 비대화형 스트림 편집기라는 정신 모델을 설명할 수 있다.
s///의 치환 플래그(g,p,i)와 주소 지정(N,M,/정규식/,!)을 조합해 원하는 줄만 골라 편집할 수 있다.- GNU sed의
-i[SUFFIX](접미사 생략 가능)와 BSD/macOS sed의-i extension(접미사 인자 필수) 차이를 설명하고, 크로스 플랫폼 스크립트에서 안전하게 처리할 수 있다. - 경로처럼
/가 반복되는 패턴을 치환할 때 구분자를 바꾸는 관용구(s|old|new|)를 쓸 수 있다. -i가 실제로는 임시 파일을 만들고 rename하는 방식으로 동작한다는 점과, 그로 인한 데이터 손실 위험을 설명할 수 있다.
![Featured image of post [Bash Shell] 14. sed - 스트림 편집](/post/bashshell/sed-command-stream-editor-linux/wordcloud_hu_3ba086147d61af39.webp)
![[Bash Shell] 12. man, history - 매뉴얼과 명령 기록](/post/bashshell/man-history-commands-manual-pages-shell-history/wordcloud_hu_7ec28326489f97a8.webp)
![[Bash Shell] 13. grep - 패턴 검색](/post/bashshell/grep-command-search-text-pattern-linux/wordcloud_hu_7ec28326489f97a8.webp)
![[Bash Shell] 14. sed - 스트림 편집](/post/bashshell/sed-command-stream-editor-linux/wordcloud_hu_7ec28326489f97a8.webp)
![[Bash Shell] 15. awk - 레코드·필드 처리](/post/bashshell/awk-command-text-processing-field-records/wordcloud_hu_7ec28326489f97a8.webp)
![[Bash Shell] 16. cut - 컬럼 추출](/post/bashshell/cut-command-extract-columns-linux/wordcloud_hu_7ec28326489f97a8.webp)
![[Bash Shell] 17. tr - 문자 치환·삭제](/post/bashshell/tr-command-translate-delete-characters/wordcloud_hu_7ec28326489f97a8.webp)
![[Bash Shell] 03. cat, head, tail - 파일 내용 출력과 일부 보기](/post/bashshell/cat-head-tail-commands-view-file-contents/wordcloud_hu_7ec28326489f97a8.webp)