sed 명령어 사용법: 스트림 편집의 기본기
스크립트를 읽다 보면 sed 's/foo/bar/g' 같은 구문이 자주 등장합니다. 뜻은 대충 짐작이 가는데, 정확한 문법이나 다양한 활용법은 막상 모르는 경우가 많죠. CI/CD 파이프라인 설정 파일이나 배포 스크립트에도 단골로 등장하고, 코딩 에이전트가 터미널에서 파일을 수정할 때도 종종 꺼내드는 도구입니다.
sed는 Stream Editor의 약자입니다. 파일이나 표준 입력으로 들어오는 텍스트를 줄 단위로 읽어서 변환한 뒤 출력하는 도구예요. 1974년 Bell Labs에서 태어났으니 grep, find와 마찬가지로 반세기가 넘는 역사를 지닌 도구입니다.
이번 글에서는 sed의 기본 문법부터 치환, 삭제, 주소 지정, 파일 직접 수정까지 실전 예제와 함께 살펴보겠습니다.
기본 문법
sed의 기본 형태는 이렇습니다.
sed '명령어' 파일
파일 대신 파이프로 표준 입력을 받을 수도 있어요.
cat 파일 | sed '명령어'
echo "텍스트" | sed '명령어'
sed는 파일을 줄 단위로 읽어서 명령어를 적용하고, 결과를 표준 출력으로 내보냅니다. 원본 파일은 손대지 않아요. 나중에 다룰 -i 옵션을 쓸 때만 파일이 직접 수정됩니다.
텍스트 치환: s 명령
sed에서 가장 많이 쓰는 명령은 s(substitute)입니다. 패턴을 찾아 다른 텍스트로 바꿔줘요.
sed 's/패턴/교체/' 파일
간단한 예를 볼까요.
sed 's/Hello/Hi/' /tmp/test.txt
Hi, World!
hello, sed!
HELLO, everyone!
첫 번째 줄의 Hello가 Hi로 바뀌었습니다. 그런데 두 번째 줄의 hello와 세 번째 줄의 HELLO는 그대로네요. sed는 대소문자를 구분하고, 각 줄에서 첫 번째 매칭만 치환합니다.
치환 플래그
치환 명령의 동작을 바꾸는 플래그를 세 번째 슬래시 뒤에 붙일 수 있습니다.
전체 치환: g (global)
g 플래그를 붙이면 한 줄에 패턴이 여러 번 나와도 전부 바꿔줍니다.
echo 'one two one two' | sed 's/one/1/g'
1 two 1 two
g를 안 붙이면 첫 번째 one만 바뀌었을 텐데, g를 붙이니 두 번 모두 치환됐습니다. 대부분의 경우 전체 치환을 원하기 때문에 g는 거의 항상 쓰게 됩니다.
대소문자 무시: I
I 플래그(GNU sed에서는 i도 됨)를 붙이면 대소문자 구분 없이 패턴을 찾습니다.
sed 's/hello/Hi/I' /tmp/test.txt
Hi, World!
Hi, sed!
Hi, everyone!
Hello, hello, HELLO 모두 Hi로 바뀌었습니다.
n번째 치환
숫자 플래그를 쓰면 n번째로 매칭된 것만 바꿉니다.
echo 'aaa bbb aaa ccc aaa' | sed 's/aaa/XXX/2'
aaa bbb XXX ccc aaa
두 번째 aaa만 XXX로 바뀌었습니다. 2와 g를 함께 쓰면(2g) 두 번째 이후 전체를 바꿀 수도 있어요.
구분자 변경
s 명령의 구분자는 슬래시(/)가 기본이지만, 다른 문자로 바꿀 수 있습니다. URL이나 파일 경로처럼 슬래시가 포함된 텍스트를 다룰 때 유용해요.
# 슬래시를 이스케이프해야 해서 읽기 어려움
sed 's/\/usr\/local\/bin/\/opt\/bin/' file.txt
# 구분자를 @ 또는 ,로 바꾸면 훨씬 깔끔
sed 's@/usr/local/bin@/opt/bin@' file.txt
sed 's,/usr/local/bin,/opt/bin,' file.txt
s 뒤에 나오는 첫 글자가 구분자가 됩니다. 슬래시 대신 @, ,, |, ! 같은 문자를 쓸 수 있어요.
줄 삭제: d 명령
d(delete) 명령은 조건에 맞는 줄을 삭제합니다. 설정 파일에서 주석을 지우거나, 로그에서 불필요한 줄을 걸러낼 때 자주 씁니다.
sed '/^#/d' /tmp/sample.txt
host=127.0.0.1
port=8080
name=myapp
#으로 시작하는 주석 줄이 모두 사라졌습니다. 빈 줄도 같은 방식으로 삭제할 수 있어요.
sed '/^$/d' /tmp/empty.txt
line1
line2
line3
^$는 시작(^)과 끝($) 사이에 아무것도 없는 빈 줄을 나타내는 정규표현식입니다. 위 두 조건을 합치면 주석과 빈 줄을 한 번에 제거할 수 있어요.
sed '/^#\|^$/d' config.txt
줄 출력: p 명령과 -n 옵션
sed는 처리한 줄을 모두 출력합니다. -n 옵션을 붙이면 이 기본 출력을 끄고, p(print) 명령으로 명시적으로 지정한 줄만 출력하게 됩니다.
sed -n '2p' /tmp/test.txt
hello, sed!
2번째 줄만 출력됐습니다. -n과 p를 조합하면 원하는 줄만 골라서 볼 수 있어요. grep 명령어가 패턴으로 줄을 검색한다면, -n과 p를 조합한 sed는 줄 번호나 범위로 줄을 뽑아낼 때 더 직관적입니다.
주소 지정: 어떤 줄에 명령을 적용할지
명령을 모든 줄이 아니라 특정 줄이나 범위에만 적용하고 싶을 때는 주소(address)를 지정합니다.
줄 번호
# 3번째 줄에만 치환 적용
sed '3s/INFO/LOG/' /tmp/log.txt
패턴
/패턴/으로 특정 내용이 포함된 줄에만 명령을 적용합니다.
# ERROR가 포함된 줄에서만 치환
sed '/ERROR/s/ERROR/🔴 ERROR/' /tmp/log.txt
범위
두 주소를 쉼표로 이어서 범위를 지정할 수 있습니다. 줄 번호와 패턴을 섞어서 쓸 수도 있어요.
# 1번째부터 2번째 줄까지만 출력
sed -n '1,2p' /tmp/test.txt
Hello, World!
hello, sed!
패턴으로 범위를 지정하는 방법도 자주 씁니다. 설정 파일에서 특정 섹션만 추출할 때 유용해요.
sed -n '/\[database\]/,/^\[/p' /tmp/config.txt
[database]
host=db.example.com
port=5432
name=mydb
[cache]
[database]가 나타나는 줄부터 다음 [로 시작하는 줄까지를 출력했습니다. 로그 파일에서 특정 요청의 시작부터 끝까지를 뽑아낼 때도 이 패턴을 쓸 수 있어요.
주소 앞에 !를 붙이면 반대 조건
# 1번째 줄을 제외하고 출력
sed '1d' /tmp/test.txt
# DEBUG가 없는 줄만 출력
sed -n '/DEBUG/!p' /tmp/log.txt
여러 명령 실행: -e
명령어 하나로 여러 변환을 한 번에 처리하고 싶다면 -e 옵션을 여러 번 쓰거나 세미콜론(;)으로 이어서 씁니다.
sed -e 's/Hello/Hi/' -e 's/everyone/all/' /tmp/test.txt
Hi, World!
hello, sed!
HELLO, all!
첫 번째 -e로 Hello를 Hi로, 두 번째 -e로 everyone을 all로 바꿨습니다. 명령어 수가 적으면 세미콜론으로 연결하는 방식도 깔끔합니다.
sed 's/Hello/Hi/;s/everyone/all/' /tmp/test.txt
파일 직접 수정: -i
앞서 본 예제들은 전부 변환 결과를 터미널에 출력했는데, -i(in-place) 옵션을 쓰면 파일을 직접 수정합니다.
sed -i 's/localhost/127.0.0.1/' config.txt
단, macOS(BSD sed)에서는 -i 다음에 백업 파일 확장자를 반드시 지정해야 합니다.
# macOS: 백업 파일 확장자 필수 (빈 문자열이면 .bak 없이 수정)
sed -i '' 's/localhost/127.0.0.1/' config.txt
# Linux(GNU sed): 확장자 없이도 됨
sed -i 's/localhost/127.0.0.1/' config.txt
백업 파일을 남기고 싶다면 확장자를 지정하세요. 예를 들어 -i.bak이라고 하면 config.txt.bak이라는 백업이 만들어집니다.
sed -i.bak 's/localhost/127.0.0.1/' config.txt
수정 전 파일이 config.txt.bak으로 보존되니까, 결과가 마음에 안 들면 되돌릴 수 있어요.
역참조: 잡은 패턴 재활용
\(, \)로 패턴의 일부를 캡처해두고, 교체 문자열에서 \1, \2 등으로 다시 쓸 수 있습니다. 이를 역참조(back-reference)라고 합니다.
echo 'John Smith' | sed 's/\(.*\) \(.*\)/\2, \1/'
Smith, John
\(.*\)가 이름, \(.*\)가 성을 각각 캡처해서 순서를 바꿨습니다. 이름/성 형식을 성/이름 형식으로 뒤집은 거죠. 날짜 형식을 YYYY-MM-DD에서 DD/MM/YYYY로 바꾸거나, 전화번호 형식을 정규화할 때도 역참조를 씁니다.
실전 활용 패턴
실제로 자주 쓰게 되는 패턴 몇 가지를 모아봤습니다.
로그에서 DEBUG 줄 제거
sed '/DEBUG/d' /tmp/log.txt
2026-04-25 10:00:01 INFO Server started
2026-04-25 10:00:03 ERROR Connection failed
2026-04-25 10:00:04 INFO Retrying...
2026-04-25 10:00:06 INFO Connection established
설정 파일 한 줄 수정
# 배포 환경에 맞게 HOST 변경
sed -i '' 's/^HOST=.*/HOST=prod.example.com/' .env
주석과 빈 줄 제거해서 실제 설정만 보기
sed '/^[#;]/d;/^$/d' /etc/ssh/sshd_config
뒤따라오는 공백 제거
sed 's/[[:space:]]*$//' file.txt
특정 섹션만 추출
sed -n '/# BEGIN CONFIG/,/# END CONFIG/p' deploy.sh
# BEGIN CONFIG
HOST=localhost
PORT=8080
DEBUG=false
# END CONFIG
파이프라인에서 필터로 활용
# 환경 변수 목록에서 PATH 관련 변수만 보고 값의 :를 줄바꿈으로 표시
env | grep PATH | sed 's/:/\n/g'
# curl 응답에서 헤더만 추출
curl -I https://example.com | sed -n '2,/^$/p'
macOS vs Linux 차이점
sed는 macOS와 Linux에서 동작이 약간 다릅니다. macOS는 BSD sed, Linux는 GNU sed를 씁니다.
가장 큰 차이는 -i 옵션입니다. macOS에서는 -i ''처럼 빈 문자열이라도 반드시 확장자를 넣어야 하고, -i.bak 같이 백업 확장자를 지정하면 양쪽에서 모두 동작합니다.
# 양쪽에서 모두 동작하는 안전한 방식
sed -i.bak 's/old/new/' file.txt
rm file.txt.bak # 백업 불필요하면 삭제
또 GNU sed는 \n(줄바꿈), \t(탭) 같은 이스케이프 시퀀스를 치환 문자열에서 바로 쓸 수 있지만, BSD sed에서는 안 되는 경우가 있습니다. 이식성이 중요한 스크립트라면 이 점을 염두에 두세요.
크로스 플랫폼 스크립트를 작성할 때는 sed 대신 Bash 파라미터 확장으로 대체할 수 있는지도 고려해볼 만합니다. 외부 프로세스를 실행하지 않아서 더 빠르고 이식성도 좋거든요.
마치며
sed는 텍스트를 변환하는 일만 합니다. 파일을 직접 열지 않아도 되고, 파이프로 다른 명령어와 자유롭게 연결되니까 스크립트에서 특히 빛을 발하죠.
처음에는 s/패턴/교체/g로 시작해서, 주소 지정과 -i 옵션을 익히는 것만으로도 실무에서 쓸 수 있는 활용법의 대부분을 커버합니다. 텍스트를 검색하는 grep, 파일을 찾는 find와 함께 파이프라인에서 세 도구를 조합하면 복잡한 텍스트 처리도 간결하게 해결할 수 있어요.
더 자세한 내용은 GNU sed 매뉴얼을 참고하세요.
This work is licensed under
CC BY 4.0