Linux 'awk'
개요
awk는 텍스트를 훑고 변환하는 작은 언어로, 로그와 CSV/TSV, 즉석 분석에 딱 맞는다. 스트리밍 스프레드시트라고 생각하면 된다. 셸을 벗어나지 않고 행을 걸러내고, 열을 골라 재배치하고, 집계를 계산하고, 결과를 출력한다.
요약 (치트시트)
- 실행:
awk 'pattern { action }' file - 필드:
$1, $2, …, 줄 전체:$0 -
내장 변수:
-
FS(입력 구분자),OFS(출력 구분자) -
RS(레코드 구분자),ORS(출력 레코드 구분자) -
NR(전체 줄 번호),FNR(파일 내 줄 번호),NF(필드 개수) -
FILENAME,ARGC,ARGV
-
- 블록:
BEGIN { … }(입력 전),END { … }(모든 입력 후) - CLI:
-F,(FS 지정),-v k=v(변수 전달),-f prog.awk(파일 사용)
매일 쓰게 되는 핵심 패턴
열 선택과 재구성
# CSV: 1번째와 3번째 열을 TSV로 출력
awk -F, '{print $1, $3}' OFS='\t' data.csv
헤더 건너뛰고 합계/평균 계산
awk -F, 'NR>1{n++; sum+=$5} END{print "count="n,"sum="sum,"avg="sum/n}' data.csv
조건 필터 (예: status ≥ 500)
awk '$9 >= 500' access.log
그룹별 집계 (사용자별 합계)
awk -F, 'NR>1{sum[$1]+=$3} END{for (u in sum) print u,sum[u]}' OFS=, tx.csv
헤더를 고려한 여러 CSV 병합
# 첫 파일의 헤더만 남기고, 나머지는 데이터 행만 출력
awk 'FNR==1 && NR!=1{next} {print}' *.csv > merged.csv
전역 치환 (로그 레벨 등)
awk '{gsub(/WARN/, "WARNING"); print}' app.log
잘못된 행 걸러내기 (열 개수 불일치 / 숫자 아님)
awk -F, 'NF>=5 && $3 ~ /^[0-9.]+$/' data.csv
고정폭 파싱
# name = 1~10번 칼럼, age = 12~14번 (뒤쪽 공백 제거)
awk '{name=substr($0,1,10); gsub(/ +$/,"",name); age=substr($0,12,3); print name,age}' OFS=, fixed.txt
중복 줄 제거
awk '!seen[$0]++' input.txt
보기 좋게 정렬된 출력
awk -F, 'NR>1{printf "%-20s %8.2f\n", $1, $3}' data.csv
CSV의 함정 (과 해법)
CSV는 까다롭다 (따옴표, 따옴표 안의 쉼표). GNU awk (gawk) 는 FPAT으로 토큰 단위 파싱을 지원한다:
gawk -v FPAT='([^,]*)|("[^"]*")' '
NR==1{print "user,total"; next}
{ amt = $3; gsub(/"/,"",$1); sum[$1]+=amt }
END{for (u in sum) print u "," sum[u]}
' data.csv
완전히 견고한 CSV/JSON 처리가 필요하면 전용 도구(
mlr,xsv,jq)를 고려하자.awk는 형식이 예측 가능할 때 쓴다.
날짜/시간 기법 (gawk)
# 입력: 2025-08-20T09:15:32 → 시간대별 버킷
gawk -F'[T:]' '{hour=$2; cnt[hour]++} END{for(h in cnt) printf "%02d,%d\n",h,cnt[h]}' events.txt | sort -t, -k1,1n
# epoch를 파싱해 사람이 읽는 시간으로 출력
gawk '{print strftime("%Y-%m-%d %H:%M:%S", $1)}' epochs.txt
성능 팁
- 파이프 여러 개보다
awk하나를 선호하라. 한 번 훑으면서 필터링과 서식을 모두 처리할 수 있다. -
거대한 데이터에서는 속도를 위해 로케일을 미리 지정하라:
LC_ALL=C awk '…' bigfile -
mawk는 아주 빠르지만 gawk의 일부 기능이 없다.busybox awk는 최소한만 제공한다.FPAT,asort()/asorti(), in-place 편집이 필요하면 gawk를 쓰자.
In-place 편집 (gawk)
# 치환 후 되쓰기 (gawk 확장)
gawk -i inplace '{gsub(/DEBUG/, "INFO"); print}' app.log
(이식성 있는 대안: 임시 파일에 쓴 뒤 mv.)
미니 쿡북
등장 횟수 기준 상위 N명
gawk '{c[$1]++} END{for(u in c) print c[u],u}' file | sort -nr | head
줄 번호로 두 파일 합치기 (2열 리포트)
paste ids.txt amounts.txt | awk -F'\t' '{print $1 "," $2}'
키(첫 필드) 기준 유일 행
awk -F, '!seen[$1]++' data.csv
HTTP 코드 히스토그램 (9번째 필드)
awk '{h[$9]++} END{for(k in h) print k,h[k]}' access.log | sort -k1,1n
awk를 쓰지 말아야 할 때
- 복잡한 CSV/JSON/XML (
mlr/jq/제대로 된 파서를 쓸 것). -
RS/ORS를 조정하지 않는 한, 개행이 들어간 다중 줄 레코드.
정리: 한 문장으로 말할 수 있다면(“헤더 건너뛰고 사용자별로 3번 열 합계”), 보통 awk 하나로 쓸 수 있다.