awk 명령어: 필드 출력과 -F 구분자, 조건 패턴 사용법

공정위 광고 문구 Ver 3.1
공정위 광고 문구 Ver 3.2(모바일 전용)
테스트 환경RHEL 계열AlmaLinux 9.8root 계정2026-10-29 확인

awk 명령어는 텍스트 파일을 한 줄씩 읽어 공백이나 지정한 구분자로 필드를 나누고, 조건에 맞는 줄의 필드만 골라 출력하거나 계산하는 리눅스 도구입니다. AlmaLinux 9 서버에서 웹 서버 접속 로그 예시 파일의 접속 IP별 요청 수와 상태 코드별 건수를 sort와 파이프로 이은 한 줄 명령으로 집계해 보니, 열두 줄 가운데 404 응답 3건이 모두 한 IP에서 온 wp-login.php와 .env 요청이었습니다.

아래 출력은 AlmaLinux 9.8 서버의 root 계정으로 작업 폴더 안에서 실행한 결과입니다. 접속 로그와 계정 목록, 판매 기록, 점수 파일은 시험용으로 만든 예시 파일이고, 그 안의 IP 주소와 계정 이름도 예시 값입니다.

1️⃣ awk 명령어 기본 사용법

  • ↪️ 필드 번호와 print

awk는 읽은 줄을 공백 기준으로 나눠 앞에서부터 $1, $2, $3 같은 필드 번호를 붙입니다. 이름과 결과, 점수가 공백으로 나뉜 scores.txt로 확인했습니다.

cat scores.txt
alice   pass  82
bob     fail  47
carol   pass  91
dave    pass  68
erin    fail  55

작은따옴표 안의 중괄호가 줄마다 실행할 동작(액션)이고, print 뒤에 필드 번호를 쉼표로 나열하면 그 필드만 나옵니다. 첫째와 셋째 필드를 출력하면 이름과 점수만 남습니다.

awk '{print $1, $3}' scores.txt
alice 82
bob 47
carol 91
dave 68
erin 55

필드 사이의 쉼표를 빼고 $1 $3으로 붙여 쓰면 두 값이 띄어쓰기 없이 이어집니다. print의 쉼표는 출력 구분자(기본값 공백 하나)를 넣는 기호이고, 쉼표 없이 나란히 쓴 값은 문자열 하나로 이어 붙습니다.

awk '{print $1 $3}' scores.txt
alice82
bob47
carol91
dave68
erin55

NR은 지금까지 읽은 줄 번호, NF는 그 줄의 필드 개수입니다. $NF는 마지막 필드를 의미하므로 필드 수가 줄마다 달라도 마지막 칸을 고를 수 있습니다.

awk '{print NR, NF, $NF}' scores.txt
1 3 82
2 3 47
3 3 91
4 3 68
5 3 55

웹 서버 접속 로그도 공백으로 나뉘므로 같은 방식으로 필드를 고릅니다. 시험용 접속 로그 첫 줄의 필드 번호는 아래와 같습니다.

필드첫 줄의 값뜻
$1192.0.2.10접속 IP
$4[29/Oct/2026:00:01:12요청 날짜와 시각
$6"GET요청 방식(앞에 큰따옴표가 붙음)
$7/index.html요청 주소
$9200상태 코드
$105120전송 바이트
$11"-"이전 페이지(Referer)
$12~$15"Mozilla/5.0 (X11; Linux x86_64)"사용자 에이전트(공백마다 나뉨)

접속 IP와 요청 주소, 상태 코드에 해당하는 $1, $7, $9를 골라 출력했습니다.

awk '{print $1, $7, $9}' access.log
192.0.2.10 /index.html 200
198.51.100.7 /about.html 200
192.0.2.10 /images/logo.png 200
203.0.113.25 /wp-login.php 404
203.0.113.25 /wp-login.php 404
192.0.2.10 /contact.html 200
198.51.100.7 /old-page.html 301
203.0.113.25 /.env 404
192.0.2.44 /index.html 200
198.51.100.7 /api/search?q=awk 500
192.0.2.44 /images/logo.png 304
192.0.2.10 /index.html 200

사용자 에이전트처럼 값 안에 공백이 있는 칸은 필드가 여러 개로 나뉩니다. 처음 세 줄의 NF는 모두 15이고, $NF는 사용자 에이전트의 마지막 조각입니다.

awk '{print NF, $NF}' access.log | head -3
15 x86_64)"
15 x86_64)"
15 x86_64)"

2️⃣ awk -F 구분자 지정

  • ↪️ 콜론, 쉼표, 정규식 구분자

공백이 아닌 기호로 칸을 나눈 파일은 -F 뒤에 그 기호를 적습니다. /etc/passwd와 같은 꼴로 만든 계정 목록 users.txt에 -F 없이 $1을 출력하자, 대부분의 줄이 통째로 나왔습니다.

awk '{print $1}' users.txt
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
nobody:x:65534:65534:Kernel
user:x:1000:1000:Example
deploy:x:1001:1001:Deploy
backup:x:1002:1002::/home/backup:/sbin/nologin

기본 구분자가 공백이라 콜론으로만 이어진 줄은 필드가 하나뿐입니다. 설명 칸에 공백이 있는 nobody, user, deploy 줄만 그 공백에서 잘립니다.

-F:로 콜론을 구분자로 지정하면 계정 이름($1)과 로그인 셸($7)이 나뉩니다.

awk -F: '{print $1, $7}' users.txt
root /bin/bash
bin /sbin/nologin
daemon /sbin/nologin
nobody /sbin/nologin
user /bin/bash
deploy /bin/bash
backup /sbin/nologin

쉼표로 나뉜 CSV 파일은 -F,로 나눕니다. 판매 기록 sales.csv의 품목과 수량을 출력하면 첫 줄의 머리글(item qty)도 같은 방식으로 나뉘어 함께 나옵니다.

awk -F, '{print $2, $3}' sales.csv
item qty
keyboard 2
mouse 5
monitor 1
keyboard 1
cable 10
mouse 2
monitor 2

-F,는 큰따옴표로 감싼 칸 안에 쉼표가 든 CSV에는 맞지 않습니다. 품목 칸에 쉼표가 든 quoted.csv는 따옴표 안의 쉼표에서도 필드가 나뉘어, 둘째 필드에 품목 이름의 앞부분만 남습니다.

awk -F, '{print $2}' quoted.csv
item
"cable
keyboard

GNU Awk는 이런 파일에 FPAT 변수를 쓰는 방법을 공식 매뉴얼에서 안내합니다. 구분자 대신 필드 하나의 모양을 정규식으로 적으면, 따옴표로 감싼 칸이 필드 하나로 나옵니다.

awk -v FPAT='([^,]*)|("[^"]*")' '{print $2}' quoted.csv
item
"cable, usb"
keyboard

구분자를 여러 개 쓰려면 대괄호 안에 정규식으로 적습니다. 콜론과 슬래시를 모두 구분자로 지정하면 $NF가 로그인 셸 경로의 마지막 이름(bash, nologin)이 됩니다.

awk -F'[:/]' '{print $1, $NF}' users.txt
root bash
bin nologin
daemon nologin
nobody nologin
user bash
deploy bash
backup nologin

3️⃣ awk 명령어 조건 패턴

  • ↪️ 정규식, 비교 연산, NR

필드를 골라도 모든 줄이 출력되므로, 필요한 줄만 남기려면 중괄호 앞에 조건(패턴)을 둡니다. 슬래시 사이에 문자열을 적으면 그 문자열이 든 줄만 고르고, 액션을 생략하면 줄 전체가 나옵니다.

awk '/wp-login/' access.log
203.0.113.25 - - [29/Oct/2026:00:04:51 +0000] "POST /wp-login.php HTTP/1.1" 404 196 "-" "Mozilla/5.0 (X11; Linux x86_64)"
203.0.113.25 - - [29/Oct/2026:00:04:52 +0000] "POST /wp-login.php HTTP/1.1" 404 196 "-" "Mozilla/5.0 (X11; Linux x86_64)"

줄을 고르는 결과는 grep 명령어와 같고, awk는 고른 줄에서 필드까지 바로 골라 출력합니다. grep으로 고른 같은 두 줄을 파이프로 awk에 넘기면 IP와 상태 코드만 남습니다.

grep wp-login access.log | awk '{print $1, $9}'
203.0.113.25 404
203.0.113.25 404

필드 값은 ==, !=, >, >=, <, <=로 비교합니다. 상태 코드($9)가 404인 줄만 골라 IP와 요청 주소를 출력했습니다.

awk '$9 == 404 {print $1, $7}' access.log
203.0.113.25 /wp-login.php
203.0.113.25 /wp-login.php
203.0.113.25 /.env

문자열과 비교할 때는 값을 큰따옴표로 감쌉니다. 둘째 필드가 pass인 이름만 골랐습니다.

awk '$2 == "pass" {print $1}' scores.txt
alice
carol
dave

숫자 비교는 계정 번호(UID)처럼 범위로 고를 때 사용합니다. UID가 1000 이상인 계정을 고르면 일반 계정과 함께 UID가 65534인 nobody도 들어갑니다.

awk -F: '$3 >= 1000 {print $1, $3}' users.txt
nobody 65534
user 1000
deploy 1001
backup 1002

조건 두 개는 &&로 묶습니다. 65534 미만 조건을 더하면 nobody가 빠지고 일반 계정 셋과 홈 폴더만 남습니다.

awk -F: '$3 >= 1000 && $3 < 65534 {print $1, $6}' users.txt
user /home/user
deploy /home/deploy
backup /home/backup

[~]는 필드가 정규식과 맞는지, [!~]는 맞지 않는지를 확인합니다. 로그인 셸($7)이 bash로 끝나는 계정과 nologin이 아닌 계정을 각각 고르면 같은 세 계정이 나옵니다.

awk -F: '$7 ~ /bash$/ {print $1}' users.txt
root
user
deploy
awk -F: '$7 !~ /nologin/ {print $1, $7}' users.txt
root /bin/bash
user /bin/bash
deploy /bin/bash

NR 조건은 줄 번호로 고릅니다. NR > 1로 CSV의 머리글 줄을 빼면 판매 기록만 남습니다.

awk -F, 'NR > 1 {print $2, $3}' sales.csv
keyboard 2
mouse 5
monitor 1
keyboard 1
cable 10
mouse 2
monitor 2

다른 명령의 출력도 파이프로 넘겨 같은 방식으로 나눕니다. ls -l 출력에서 파일 크기($5)와 이름($9)을 고르자 첫 줄이 빈 줄로 나왔습니다. ls -l의 첫 줄 [total 24]에는 다섯째와 아홉째 필드가 없기 때문입니다.

ls -l | awk '{print $5, $9}'

1567 access.log
79 quoted.csv
207 sales.csv
85 scores-copy.txt
85 scores.txt
315 users.txt

NR > 1을 붙이면 첫 줄이 빠지고 파일 크기와 이름만 남습니다.

ls -l | awk 'NR > 1 {print $5, $9}'
1567 access.log
79 quoted.csv
207 sales.csv
85 scores-copy.txt
85 scores.txt
315 users.txt

4️⃣ awk BEGIN END와 합계, 평균

  • ↪️ 변수와 printf 출력 형식

BEGIN 블록은 첫 줄을 읽기 전에 한 번, END 블록은 마지막 줄을 처리한 뒤 한 번 실행됩니다. 합계는 줄마다 변수에 더해 두었다가 END에서 출력합니다. 아래 그림은 판매 기록의 금액 합계를 구할 때 awk가 줄마다 거치는 순서입니다.

🔁 awk가 sales.csv를 한 줄씩 처리하는 순서
설명 다이어그램
🔁 처리 순서
$ awk -F, 'NR > 1 {t += $3 * $4} END {printf "total %.2f\n", t}' sales.csv
① 읽은 줄NR = 1date,item,qty,priceNR = 22026-10-01,keyboard,2,45.00NR = 32026-10-01,mouse,5,19.90NR = 82026-10-04,monitor,2,189.00더 읽을 줄 없음8번째 줄이 마지막 줄입니다.
② 필드 나누기 (-F,)$1date$2item$3qty$4price머리글 줄도 같은 방식으로 나눕니다.$12026-10-01$2keyboard$32$445.00$3 수량, $4 단가$12026-10-01$2mouse$35$419.90$3 수량, $4 단가$12026-10-04$2monitor$32$4189.00$3 수량, $4 단가나눌 줄 없음END 블록은 줄을 읽지 않습니다.
③ 패턴과 액션NR > 1 거짓액션을 건너뜁니다.t = (빈 값)NR > 1 참t += 2 * 45.00t = 90NR > 1 참t += 5 * 19.90t = 189.5NR > 1 참t += 2 * 189.00t = 886.3END 실행total 886.30printf가 t를 소수 둘째 자리까지 출력합니다.
첫 줄은 머리글 줄이라 NR > 1이 거짓이 되고, 액션 없이 다음 줄로 넘어갑니다.
둘째 줄부터 패턴이 참이 되어, 수량과 단가를 곱한 90이 변수 t에 더해집니다.
변수 t는 줄이 바뀌어도 값을 유지하므로, 셋째 줄까지 더한 값은 189.5입니다.
4~7행도 같은 순서로 처리되어, 마지막 8행까지 더한 t는 886.3입니다.
마지막 줄을 처리한 뒤 END 블록이 한 번 실행되고, printf가 total 886.30을 출력합니다.

접속 로그의 전송 바이트($10)를 모두 더한 결과는 41298바이트였습니다. 처음 쓰는 변수는 빈 값(숫자로는 0)에서 시작하므로 따로 0을 넣지 않아도 됩니다.

awk '{sum += $10} END {print sum}' access.log
41298

합계를 END가 아닌 줄마다 실행하는 액션에서 출력하면, 줄을 읽을 때마다 그때까지 더한 값이 나옵니다. 수량과 단가를 곱해 t에 더할 때마다 줄 번호와 t를 함께 출력하면 머리글을 뺀 일곱 줄이 나오고, 마지막 줄의 886.3이 전체 합계입니다.

awk -F, 'NR > 1 {t += $3 * $4; print NR, t}' sales.csv
2 90
3 189.5
4 378.5
5 423.5
6 468.5
7 508.3
8 886.3

평균은 END에서 합계를 줄 수로 나눠 구합니다. 점수 다섯 개의 합은 343, 평균은 68.6이었습니다.

awk '{total += $3; n++} END {print total, n, total / n}' scores.txt
343 5 68.6

BEGIN은 결과 위에 머리글 줄을 한 번 출력할 때 사용합니다. 품목과 금액(수량 × 단가) 위에 item total 줄이 한 번만 나옵니다.

awk -F, 'BEGIN {print "item", "total"} NR > 1 {print $2, $3 * $4}' sales.csv
item total
keyboard 90
mouse 99.5
monitor 189
keyboard 45
cable 45
mouse 39.8
monitor 378

출력 구분자는 BEGIN에서 OFS 변수로 바꿉니다. OFS를 쉼표로 바꾸면 print의 쉼표 자리에 공백 대신 쉼표가 들어가, 계정 목록이 CSV 꼴로 나옵니다.

awk 'BEGIN {FS = ":"; OFS = ","} {print $1, $3, $7}' users.txt
root,0,/bin/bash
bin,1,/sbin/nologin
daemon,2,/sbin/nologin
nobody,65534,/sbin/nologin
user,1000,/bin/bash
deploy,1001,/bin/bash
backup,1002,/sbin/nologin

자릿수를 맞추려면 printf를 사용합니다. %-10s는 10칸 왼쪽 맞춤 문자열, %3d는 3칸 정수, %8.2f는 소수 둘째 자리까지 8칸에 맞춘 숫자입니다.

awk -F, 'NR > 1 {printf "%-10s %3d %8.2f\n", $2, $3, $3 * $4}' sales.csv
keyboard     2    90.00
mouse        5    99.50
monitor      1   189.00
keyboard     1    45.00
cable       10    45.00
mouse        2    39.80
monitor      2   378.00

printf는 print와 달리 줄을 바꾸지 않으므로 형식 끝에 [\n]을 적습니다. [\n] 없이 실행하면 품목 이름이 한 줄로 이어집니다.

awk -F, 'NR > 1 {printf "%s ", $2}' sales.csv
keyboard mouse monitor keyboard cable mouse monitor

5️⃣ awk 명령어 로그 분석과 옵션

  • ↪️ 접속 IP와 상태 코드 집계, 옵션 정리

🔲 로그 분석

변수 하나에 더하는 방식으로는 합계 하나만 나오므로, 접속 IP별, 상태 코드별로 나눠 세려면 배열에 값을 모읍니다. n[$1]++는 IP를 키로 한 배열 칸에 1씩 더하고, END의 for (ip in n)이 모인 칸을 하나씩 꺼냅니다.

awk '{n[$1]++} END {for (ip in n) print n[ip], ip}' access.log | sort -rn
4 192.0.2.10
3 203.0.113.25
3 198.51.100.7
2 192.0.2.44

for (ip in n)이 꺼내는 순서는 정해져 있지 않아(GNU Awk 매뉴얼 기준), sort -rn으로 요청 수가 많은 순서로 정렬했습니다. 192.0.2.10이 4건으로 가장 많았습니다.

상태 코드별 건수도 같은 방식으로 셉니다. 404가 3건, 500이 1건이었습니다.

awk '{n[$9]++} END {for (s in n) print s, n[s]}' access.log | sort
200 6
301 1
304 1
404 3
500 1

상태 코드만 뽑아 sort와 uniq -c로 넘겨도 같은 건수가 나옵니다.

awk '{print $9}' access.log | sort | uniq -c
      6 200
      1 301
      1 304
      3 404
      1 500

오류 응답만 보려면 상태 코드에 조건을 겁니다. 400 이상인 줄은 wp-login.php와 .env를 찾는 요청 세 건과 500 오류 한 건이었습니다.

awk '$9 >= 400 {print $9, $1, $7}' access.log
404 203.0.113.25 /wp-login.php
404 203.0.113.25 /wp-login.php
404 203.0.113.25 /.env
500 198.51.100.7 /api/search?q=awk

IP별 전송 바이트 합계도 배열에 더해 구합니다. 192.0.2.10이 31340바이트로 가장 많이 받았습니다.

awk '{b[$1] += $10} END {for (ip in b) print b[ip], ip}' access.log | sort -rn
31340 192.0.2.10
5120 192.0.2.44
4250 198.51.100.7
588 203.0.113.25

시각 필드($4)는 문자열로 비교할 수 있습니다. 날짜와 형식이 같은 줄끼리는 문자열 순서가 시각 순서와 같아, 00:05부터 00:10 전까지의 요청 네 건만 골랐습니다.

awk '$4 >= "[29/Oct/2026:00:05" && $4 < "[29/Oct/2026:00:10" {print $4, $7}' access.log
[29/Oct/2026:00:05:30 /contact.html
[29/Oct/2026:00:07:02 /old-page.html
[29/Oct/2026:00:07:15 /.env
[29/Oct/2026:00:09:48 /index.html

🔲 awk 명령어 옵션

📌 주요 옵션: 이 글에서 실행한 결과

옵션하는 일이 글에서 본 결과
-F fs입력 필드 구분자를 지정-F:로 users.txt의 계정 이름($1)과 로그인 셸($7)이 나뉨, -F,로 sales.csv의 품목과 수량이 머리글(item qty)과 함께 나옴
-v var=val프로그램 실행 전에 변수에 값을 지정셸 변수 min을 -v min=$min으로 넘겨 전송 바이트가 1000 이상인 여섯 줄 출력, -v FPAT=…로 quoted.csv의 따옴표로 감싼 칸이 필드 하나로 나옴
-i inplace출력을 화면 대신 원래 파일에 저장gawk -i inplace 실행 뒤 화면에는 아무것도 나오지 않고 scores.txt 사본이 이름과 점수 두 칸으로 바뀜, -v inplace::suffix=.bak를 함께 붙이면 바뀌기 전 내용이 .bak 파일로 남음
--version버전 정보를 출력[awk --version] 첫 줄은 GNU Awk 5 버전

📎 그 밖의 옵션: 공식 매뉴얼 기준

옵션하는 일
-f program-fileAWK 프로그램을 첫 번째 명령줄 인자 대신 파일에서 읽음, -f를 여러 번 지정 가능
-e program-text명령줄에 적은 글자를 AWK 프로그램 소스로 사용, -f나 --include로 불러온 라이브러리 함수와 함께 사용 가능
-E file-f와 비슷하지만 마지막에 처리되는 옵션이며 명령줄 변수 대입을 막음, #! 스크립트용
--traditional호환 모드로 실행, GNU 확장 기능을 인식하지 않음
--posix호환 모드를 켜고 제한을 더함, 예를 들어 function의 동의어 func를 인식하지 않음
--lint의심스럽거나 다른 AWK 구현으로 옮기기 어려운 구문을 경고, --lint=fatal이면 경고가 치명적 오류가 됨
--dump-variables전역 변수의 타입과 최종 값을 정렬한 목록을 파일로 출력, 파일을 지정하지 않으면 awkvars.out

🔲 awk 명령어 시뮬레이터

아래 시뮬레이터에서 같은 시험 파일에 awk 명령을 직접 입력해 볼 수 있습니다.

awk 명령어 시뮬레이터GNU Awk 5
[root@server work]#
브라우저 안에서만 동작하는 작은 awk 해석기입니다. 시험 파일 넷은 실측에 쓴 파일과 같고, ↑↓ 로 앞에서 쓴 명령을 다시 불러옵니다.

🔢 FAQ & 추천 콘텐츠

작은따옴표 안의 $min은 셸이 바꾸지 않고 그대로 awk에 넘어가므로, -v 옵션으로 넘깁니다. 셸 변수 min에 1000을 넣고 -v min=$min으로 넘기면 전송 바이트가 1000 이상인 여섯 줄이 나옵니다. awk 프로그램을 큰따옴표로 감싸면 [print $1]의 $1까지 셸이 먼저 빈 값으로 바꿔, 줄 전체가 출력됩니다.

GNU Awk는 -i inplace를 붙이면 출력을 화면 대신 원래 파일에 저장합니다. scores.txt 사본에 첫째와 셋째 필드를 출력하는 프로그램을 gawk -i inplace로 실행하자 화면에는 아무것도 나오지 않았고, 파일이 이름과 점수 두 칸으로 바뀌었습니다. 원래 파일을 바로 덮어쓰므로, -v inplace::suffix=.bak를 함께 붙이면 바뀌기 전 내용이 .bak 파일로 남습니다.

AlmaLinux 9 서버에서 /usr/bin/awk는 gawk를 가리키는 심볼릭 링크였고, [awk --version] 첫 줄은 GNU Awk 5 버전이었습니다. 이 서버에서는 두 이름이 같은 프로그램을 실행합니다. -i inplace나 FPAT 같은 GNU Awk 기능을 쓰기 전에는 [awk --version]으로 GNU Awk인지 확인합니다.

ℹ️ 제휴 안내
본 사이트의 콘텐츠에는 제휴 링크가 포함되어 있습니다. 방문자가 이 링크를 통해 상품 또는 서비스를 구매하면 본 사이트는 판매처로부터 수수료를 지급받습니다. 이 과정에서 구매자가 지불하는 금액(이벤트 할인 시 금액이 내려갑니다↓)은 오르지 않습니다. 게시된 가격·할인·재고 정보는 작성 시점 기준이며 실제와 다를 수 있으므로, 구매 전 판매처에서 최종 확인하시기 바랍니다. 상품 선정과 평가는 자체 기준에 따라 작성되며, 수수료 지급 여부가 소개 순서나 평가 내용에 영향을 주지 않습니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

인간임을 증명하세요: 4   +   6   =