awk 指令:欄位輸出、-F 分隔符號與條件樣式用法
awk 指令是逐行讀取文字檔,依空白或你指定的分隔符號把每行切成欄位,再只挑出符合條件的行的欄位來輸出或計算的 Linux 工具。我們在 AlmaLinux 9 伺服器上,把網頁伺服器存取記錄範例檔中各 IP 的請求數與各狀態碼的筆數,用 sort 與管線串成的一行指令統計,發現十二行中的 3 筆 404 回應都是來自同一個 IP、對 wp-login.php 與 .env 的請求。
以下輸出是在 AlmaLinux 9.8 伺服器上,以 root 帳號於工作資料夾內執行的結果。存取記錄、帳號清單、銷售紀錄與分數檔都是測試用的範例檔,其中的 IP 位址與帳號名稱也是範例值。
1️⃣ awk 指令基本用法
awk 會依空白切開讀到的行,並從前面依序編上 $1、$2、$3 這樣的欄位編號。我們用姓名、結果與分數以空白分開的 scores.txt 來確認。
cat scores.txtalice pass 82
bob fail 47
carol pass 91
dave pass 68
erin fail 55單引號內的大括號是每行要執行的動作,在 print 後面用逗號列出欄位編號,就只輸出那些欄位。輸出第一與第三欄位時,只留下姓名與分數。
awk '{print $1, $3}' scores.txtalice 82
bob 47
carol 91
dave 68
erin 55拿掉欄位之間的逗號寫成 $1 $3,兩個值會不留空白地連在一起。print 的逗號會放入輸出分隔符號(預設為一個空白),不加逗號並排的值則會接成一個字串。
awk '{print $1 $3}' scores.txtalice82
bob47
carol91
dave68
erin55NR 是目前讀到的行號,NF 是該行的欄位數。$NF 代表最後一個欄位,因此就算每行欄位數不同,也能選到最後一欄。
awk '{print NR, NF, $NF}' scores.txt1 3 82
2 3 47
3 3 91
4 3 68
5 3 55網頁伺服器存取記錄也是以空白分開,選欄位的方式相同。測試用存取記錄第一行的欄位編號如下。
| 欄位 | 第一行的值 | 說明 |
|---|---|---|
| $1 | 192.0.2.10 | 連線 IP |
| $4 | [29/Oct/2026:00:01:12 | 請求日期與時間 |
| $6 | "GET | 請求方法(前面帶雙引號) |
| $7 | /index.html | 請求網址 |
| $9 | 200 | 狀態碼 |
| $10 | 5120 | 傳送位元組 |
| $11 | "-" | 上一頁(Referer) |
| $12~$15 | "Mozilla/5.0 (X11; Linux x86_64)" | 使用者代理(每個空白都會切開) |
我們選出並輸出相當於連線 IP、請求網址與狀態碼的 $1、$7、$9。
awk '{print $1, $7, $9}' access.log192.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 -315 x86_64)"
15 x86_64)"
15 x86_64)"2️⃣ 用 awk -F 指定分隔符號
欄位以空白以外的符號分開的檔案,要在 -F 後面寫上該符號。對格式與 /etc/passwd 相同的帳號清單 users.txt 不加 -F 輸出 $1 時,大部分的行都整行輸出。
awk '{print $1}' users.txtroot: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)與登入 Shell($7)就會分開。
awk -F: '{print $1, $7}' users.txtroot /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.csvitem qty
keyboard 2
mouse 5
monitor 1
keyboard 1
cable 10
mouse 2
monitor 2-F, 不適合用於雙引號欄位內含逗號的 CSV。品項欄含逗號的 quoted.csv,連引號內的逗號也會切開欄位,第二欄位只留下品項名稱的前半段。
awk -F, '{print $2}' quoted.csvitem
"cable
keyboardGNU Awk 官方手冊介紹了對這類檔案使用 FPAT 變數的方法。不寫分隔符號,改用規則運算式寫出一個欄位的樣子,以引號包住的欄就會成為一個欄位。
awk -v FPAT='([^,]*)|("[^"]*")' '{print $2}' quoted.csvitem
"cable, usb"
keyboard要使用多個分隔符號,就在方括號內以規則運算式寫出。把冒號與斜線都指定為分隔符號後,$NF 會變成登入 Shell 路徑的最後一個名稱(bash、nologin)。
awk -F'[:/]' '{print $1, $NF}' users.txtroot bash
bin nologin
daemon nologin
nobody nologin
user bash
deploy bash
backup nologin3️⃣ awk 指令條件樣式
即使選了欄位,所有行仍會輸出,因此要只留下需要的行,就在大括號前放條件(樣式)。在斜線之間寫字串就只選出含該字串的行,省略動作則輸出整行。
awk '/wp-login/' access.log203.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.log203.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.txtalice
carol
dave數字比較用於依範圍選取,例如帳號編號(UID)。選取 UID 1000 以上的帳號時,除了一般帳號,UID 為 65534 的 nobody 也會包含在內。
awk -F: '$3 >= 1000 {print $1, $3}' users.txtnobody 65534
user 1000
deploy 1001
backup 1002兩個條件用 && 連接。加上小於 65534 的條件後,nobody 就被排除,只留下三個一般帳號與家目錄。
awk -F: '$3 >= 1000 && $3 < 65534 {print $1, $6}' users.txtuser /home/user
deploy /home/deploy
backup /home/backup[~] 確認欄位是否符合規則運算式,[!~] 確認是否不符合。分別選取登入 Shell($7)以 bash 結尾的帳號與不是 nologin 的帳號,會出現相同的三個帳號。
awk -F: '$7 ~ /bash$/ {print $1}' users.txtroot
user
deployawk -F: '$7 !~ /nologin/ {print $1, $7}' users.txtroot /bin/bash
user /bin/bash
deploy /bin/bashNR 條件依行號選取。用 NR > 1 去掉 CSV 的標題行後,只留下銷售紀錄。
awk -F, 'NR > 1 {print $2, $3}' sales.csvkeyboard 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.txt4️⃣ awk BEGIN、END 與加總、平均
BEGIN 區塊在讀第一行之前執行一次,END 區塊在處理完最後一行後執行一次。加總時每行累加到變數,再於 END 輸出。下圖是計算銷售紀錄金額合計時,awk 每行經過的順序。
$ awk -F, 'NR > 1 {t += $3 * $4} END {printf "total %.2f\n", t}' sales.csv存取記錄的傳送位元組($10)全部加總為 41298 位元組。第一次使用的變數從空值(數字為 0)開始,所以不必另外設成 0。
awk '{sum += $10} END {print sum}' access.log41298把合計放在每行執行的動作而不是 END 中輸出,每讀一行就會出現當時為止的累計。每次把數量乘單價累加到 t 時,一起輸出行號與 t,就會出現不含標題的七行,最後一行的 886.3 就是總計。
awk -F, 'NR > 1 {t += $3 * $4; print NR, t}' sales.csv2 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.txt343 5 68.6BEGIN 用於在結果上方輸出一次標題行。品項與金額(數量 × 單價)上方只出現一次 item total 行。
awk -F, 'BEGIN {print "item", "total"} NR > 1 {print $2, $3 * $4}' sales.csvitem 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.txtroot,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 %8.2f\n", $2, $3, $3 * $4}' sales.csvkeyboard 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.00printf 和 print 不同,不會換行,所以要在格式最後寫上 [\n]。不加 [\n] 執行時,品項名稱會接成一行。
awk -F, 'NR > 1 {printf "%s ", $2}' sales.csvkeyboard mouse monitor keyboard cable mouse monitor5️⃣ awk 指令記錄分析與選項
🔲 記錄分析
累加到一個變數只能得到一個合計,要依連線 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 -rn4 192.0.2.10
3 203.0.113.25
3 198.51.100.7
2 192.0.2.44for (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 | sort200 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.log404 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 -rn31340 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)與登入 Shell($7),-F, 輸出 sales.csv 的品項與數量,連標題(item qty)一起出現 |
| -v var=val | 在程式執行前為變數指定值 | 將 Shell 變數 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-file | 從檔案讀取 AWK 程式,而不是第一個命令列引數;-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 指令。
🔢 FAQ & 推薦內容
ℹ️ 聯盟行銷說明
本站內容包含聯盟行銷連結。訪客透過這些連結購買商品或服務時,本站會從賣家獲得佣金。在這個過程中,購買者支付的金額(遇到活動折扣時金額會降低↓)並不會因此提高。刊登的價格、折扣與庫存資訊以撰寫當時為準,可能與實際情況不同,購買前請向賣家做最後確認。商品的挑選與評價依本站自訂的標準撰寫,是否支付佣金不會影響介紹順序或評價內容。