sy/dev
Study
13 min read

리눅스 텍스트 처리 — sed, awk, cut, sort, uniq, xargs 실전 정리

파이프로 연결되는 7~8개의 작은 도구가 곧 데이터 처리 언어다. 한 줄짜리 명령으로 로그를 분석하고 CSV를 다듬는 실전 패턴 위주로 정리.

공부하게 된 계기

한 번쯤 이런 경험이 있다.

"이 100MB짜리 로그에서 IP별 접속 횟수 상위 10개만 뽑아주세요."

Excel을 켜고, 데이터를 임포트하고, 피벗 테이블을 만든다. 30분이 사라진다.

리눅스 셸에서는 한 줄이면 된다.

awk '{print $1}' access.log | sort | uniq -c | sort -rn | head

이게 가능한 이유는 작은 도구들을 파이프로 엮는 철학 때문이다. 각 도구는 한 가지만 잘하고, 입력은 stdin, 출력은 stdout. 이걸 |로 이어붙이면 그 자체가 데이터 처리 언어다.

이 글은 그 묶음을 정리한다 — cut, sort, uniq, tr, sed, awk, xargs, 그리고 부수적으로 wc, paste, comm, diff.

1) cut — 컬럼 자르기

# 구분자 기준으로 N번째 컬럼
cut -d',' -f2 data.csv
 
# 여러 컬럼
cut -d',' -f1,3,5 data.csv
 
# 범위
cut -d',' -f2-4 data.csv
 
# 탭 구분 (기본)
cut -f1,2 tabbed.tsv
 
# 문자 위치로 자르기
cut -c1-10 file.txt

가장 단순한 컬럼 추출. CSV·TSV의 특정 컬럼만 빠르게 빼낼 때.

공백이 여러 개로 구분된 경우(예: ps 출력)는 cut -d' '이 잘 안 먹는다 — 공백이 여러 개면 빈 컬럼이 끼기 때문. 이런 경우엔 awk가 답.

2) sort — 정렬

# 알파벳 순
sort file.txt
 
# 역순
sort -r file.txt
 
# 숫자로 정렬 (그냥 sort는 문자열로 정렬해서 "10"이 "2"보다 앞)
sort -n file.txt
 
# 큰 숫자 순
sort -rn file.txt
 
# 특정 컬럼 기준
sort -k2 file.txt           # 2번째 컬럼
sort -t',' -k3 -n data.csv  # CSV의 3번째 컬럼, 숫자
 
# 중복 제거하면서 정렬
sort -u file.txt
 
# 사람이 읽는 단위 (1K, 1M, 1G)
sort -h
du -sh */ | sort -rh        # 큰 디렉토리 순

sort -hdu -sh 결과 정렬에 거의 매번 쓴다.

3) uniq — 중복 처리

uniq연속된 중복만 제거한다. 그래서 거의 항상 sort와 같이 쓴다.

# 중복 제거
sort file.txt | uniq
 
# 카운트 (각 줄이 몇 번 나왔는지)
sort file.txt | uniq -c
 
# 중복인 줄만
sort file.txt | uniq -d
 
# 유일한 줄만 (한 번만 등장)
sort file.txt | uniq -u

IP별 접속 카운트 — 표준 패턴

awk '{print $1}' access.log | sort | uniq -c | sort -rn | head

한 줄에서 다섯 단계가 일어난다:

  1. awk '{print $1}' — 1번째 컬럼 (IP)
  2. sort — 정렬해서 같은 IP끼리 모음
  3. uniq -c — 카운트
  4. sort -rn — 카운트 큰 순
  5. head — 상위 10개

이 패턴이 셸 텍스트 처리의 정석. 매번 보게 된다.

4) tr — 문자 치환·삭제

# 대소문자 변환
echo "Hello" | tr 'a-z' 'A-Z'   # HELLO
echo "Hello" | tr 'A-Z' 'a-z'   # hello
 
# 특정 문자 삭제
echo "abc-123" | tr -d '-'      # abc123
 
# 여러 공백을 하나로
echo "a    b   c" | tr -s ' '   # a b c
 
# 줄바꿈을 다른 걸로
cat file.txt | tr '\n' ','      # CSV로
 
# CRLF → LF (Windows 파일 → Unix)
tr -d '\r' < windows.txt > unix.txt

sed로도 다 할 수 있지만 단순한 문자 치환은 tr이 더 빠르고 직관적.

5) sed — 스트림 에디터

sed는 강력하지만 처음엔 부담스럽다. 일단 자주 쓰는 5개 패턴만 외우면 80%는 된다.

패턴 1: 치환

# 첫 번째 매치만 (한 줄에 여러 개 있어도 첫 개만)
sed 's/old/new/' file.txt
 
# 모두 (g = global)
sed 's/old/new/g' file.txt
 
# 대소문자 무시
sed 's/old/new/gI' file.txt

패턴 2: 파일 직접 수정 (-i)

sed -i 's/old/new/g' file.txt          # Linux
sed -i '' 's/old/new/g' file.txt       # macOS (BSD sed — 빈 따옴표 필요)

패턴 3: 특정 줄 삭제

sed '5d' file.txt                # 5번째 줄
sed '5,10d' file.txt             # 5~10번째 줄
sed '/^$/d' file.txt             # 빈 줄 모두
sed '/pattern/d' file.txt        # pattern 매치되는 줄

패턴 4: 특정 줄만 출력

sed -n '10,20p' file.txt         # 10~20번째 줄만
sed -n '/ERROR/p' log.txt        # ERROR 포함된 줄만

-n은 "기본 출력 끔", p는 "이 줄 출력".

패턴 5: 정규식과 캡처

# 그룹 캡처해서 재배열
sed -E 's/([0-9]+)-([a-z]+)/\2-\1/' file.txt
# 123-abc  →  abc-123

-E(BSD/GNU 모두 지원)로 확장 정규식 활성화.

⚠️

macOS의 sed는 GNU sed와 미묘하게 다르다. -i ''처럼 빈 인자가 필요한 점이 가장 큰 차이. 스크립트를 둘 다에서 쓰려면 gsed(brew install gnu-sed)를 깔거나, 양쪽 호환되는 문법만 쓰는 게 안전하다.

6) awk — 한 줄짜리 데이터 처리 언어

awk는 사실 언어다. 하지만 우리가 쓰는 99%는 한 줄짜리.

가장 자주 쓰는 형태

# 컬럼 출력 (기본 구분자: 공백)
awk '{print $1}' file.txt
awk '{print $1, $3}' file.txt        # 1, 3번째
awk '{print $NF}' file.txt           # 마지막 컬럼
 
# 구분자 변경
awk -F',' '{print $2}' data.csv
awk -F':' '{print $1}' /etc/passwd

조건 + 액션

# 9번째 컬럼이 5로 시작하는 줄만 (5xx 응답)
awk '$9 ~ /^5/' access.log
 
# 9번째 컬럼이 500 이상
awk '$9 >= 500' access.log
 
# 두 컬럼 비교
awk '$3 > $4' data.txt
 
# 매칭 줄 카운트
awk '/ERROR/ {count++} END {print count}' log.txt

합계·평균

# 10번째 컬럼 합계
awk '{sum += $10} END {print sum}' file.txt
 
# 평균
awk '{sum += $10; n++} END {print sum/n}' file.txt
 
# 최댓값
awk 'NR==1 || $1 > max {max=$1} END {print max}' file.txt

멀티라인 — BEGIN, END

awk '
  BEGIN { print "Start" }
  /ERROR/ { errors++ }
  /WARN/ { warnings++ }
  END { print "ERROR:", errors, "WARN:", warnings }
' log.txt

awk만으로 책 한 권이 나오니, 일단 {print $N}과 조건 필터링 두 가지만 자유롭게 쓰면 그 자체로 강력하다.

7) xargs — 출력을 다른 명령의 인자로

파이프는 보통 stdin → stdout이지만, 어떤 명령은 인자로만 입력을 받는다 (rm, mv, cp 등). xargs가 그 다리 역할.

# 모든 .log 파일 삭제
find . -name '*.log' | xargs rm
 
# 더 안전하게 (공백 포함 파일명 대응)
find . -name '*.log' -print0 | xargs -0 rm
 
# 한 번에 하나씩 (병렬 안 함)
find . -name '*.log' | xargs -I {} mv {} /backup/
 
# 병렬 실행 (-P)
find . -name '*.png' | xargs -P 4 -I {} convert {} {}.webp

-I {} — 위치 지정

기본은 끝에 인자가 붙는다. -I {}로 위치를 바꿀 수 있다.

# 끝에 붙음
echo "src.txt" | xargs cp dest/        # cp dest/ src.txt — 잘못됨
 
# 위치 지정
echo "src.txt" | xargs -I {} cp {} dest/     # cp src.txt dest/

find -execxargs 어떤 걸: 단순한 경우 xargs가 빠르고 (한 번에 묶어 실행), 복잡한 경우 find -exec이 안전하다 (파일명에 특수문자 포함 시). 안전 우선이면 find -print0 \| xargs -0.

8) 그 외 자주 만나는 작은 도구들

wc — 카운트

wc -l file.txt    # 줄 수
wc -w file.txt    # 단어 수
wc -c file.txt    # 바이트 수

grep -c도 줄 수를 세지만, wc -l이 더 직관적인 경우가 많다.

paste — 옆으로 붙이기

# 두 파일을 컬럼으로 옆에 붙임
paste a.txt b.txt
 
# 구분자 변경
paste -d',' a.txt b.txt

comm — 두 정렬된 파일 비교

comm a.txt b.txt
# 1번 컬럼: a에만, 2번: b에만, 3번: 둘 다
 
# a에만 있는 줄
comm -23 a.txt b.txt

diff — 두 파일 차이

diff a.txt b.txt
diff -u a.txt b.txt    # unified diff (git 스타일)
diff -r dir1 dir2      # 디렉토리 비교

head / tail

3편 로그 글에서 다뤘다. 텍스트 처리에서도 자주 같이 등장.

실전 패턴 8개

A. IP별 접속 카운트 (반복)

awk '{print $1}' access.log | sort | uniq -c | sort -rn | head

B. 5xx 응답 발생 시각

awk '$9 ~ /^5/ {print $4}' access.log | head

C. 가장 큰 파일 10개

find . -type f -printf '%s %p\n' 2>/dev/null | sort -rn | head | awk '{printf "%.1fMB\t%s\n", $1/1024/1024, $2}'

D. 코드에서 TODO 카운트 (사람별)

git blame과 조합:

git ls-files | xargs grep -nE 'TODO|FIXME' \
  | awk -F: '{print $1":"$2}' \
  | xargs -I {} sh -c 'echo $(git blame -L $(echo {}|cut -d: -f2),$(echo {}|cut -d: -f2) $(echo {}|cut -d: -f1) | awk -F"(" "{print \$2}" | awk "{print \$1, \$2}")' \
  | sort | uniq -c | sort -rn

(이런 게 가능하다는 데모 — 실전에선 더 단순하게 분리하는 게 나음.)

E. CSV에서 중복 제거 (특정 컬럼 기준)

# 1번째 컬럼 기준 첫 등장만
awk -F',' '!seen[$1]++' data.csv

!seen[$1]++ 패턴 — 처음 보면 신기하지만 awk 쓰는 사람의 일상 표현.

F. 빈 줄과 주석 제거 (설정 파일 깔끔하게)

sed -e '/^#/d' -e '/^$/d' nginx.conf

G. 단어별 카운트 (가장 많이 쓰인 단어)

cat README.md | tr -s '[:space:]' '\n' | tr 'A-Z' 'a-z' | sort | uniq -c | sort -rn | head

H. 두 디렉토리에 있는 파일 차이

diff <(ls dir1) <(ls dir2)
# 또는
comm -23 <(ls dir1 | sort) <(ls dir2 | sort)

<(...)는 프로세스 치환 — 명령 출력을 임시 파일처럼 쓴다. 자주 활용된다.

정리

가장 자주 쓰는 5개:

  1. awk '{print $N}' — 컬럼 추출
  2. sort \| uniq -c \| sort -rn — 카운트 집계
  3. sed 's/old/new/g' — 치환
  4. tr — 단순 문자 변환
  5. xargs — 출력 → 인자

    이 다섯이 손에 익으면 Excel/스크립트 켤 일이 절반 이하로 줄어든다.

UNIX 철학 — "한 가지 일을 잘하는 작은 도구를 파이프로 엮는다" — 의 가장 빛나는 영역이 텍스트 처리다. 각 도구는 단순하지만, 조합의 표현력은 거의 무한하다.

시리즈 정리

8편으로 시리즈를 마무리한다.

여기까지 8편이면 일상의 90% 이상을 커버한다. 더 깊은 영역(systemd 유닛 작성, iptables, 컨테이너 런타임, eBPF 등)은 별도 시리즈로 나누는 게 자연스럽다.

참고 자료

Comments