awk 指令:欄位輸出、-F 分隔符號與條件樣式用法

測試環境RHEL 系列AlmaLinux 9.8root 帳號2026-10-29 確認

awk 指令是逐行讀取文字檔,依空白或你指定的分隔符號把每行切成欄位,再只挑出符合條件的行的欄位來輸出或計算的 Linux 工具。我們在 AlmaLinux 9 伺服器上,把網頁伺服器存取記錄範例檔中各 IP 的請求數與各狀態碼的筆數,用 sort 與管線串成的一行指令統計,發現十二行中的 3 筆 404 回應都是來自同一個 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)與登入 Shell($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 會變成登入 Shell 路徑的最後一個名稱(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

[~] 確認欄位是否符合規則運算式,[!~] 確認是否不符合。分別選取登入 Shell($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.3執行 ENDtotal 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 %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)與登入 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 指令。

awk 指令模擬器GNU Awk 5
[root@server work]#
只在瀏覽器內運作的小型 awk 直譯器。四個測試檔與實測使用的檔案相同,用 ↑↓ 可以叫回先前輸入的指令。

🔢 FAQ & 推薦內容

單引號內的 $min 不會被 Shell 置換,會原樣傳給 awk,所以要用 -v 選項傳入。在 Shell 變數 min 放入 1000,再以 -v min=$min 傳入,就會出現傳送位元組 1000 以上的六行。用雙引號包住 awk 程式時,Shell 會先把 [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。

ℹ️ 聯盟行銷說明
本站內容包含聯盟行銷連結。訪客透過這些連結購買商品或服務時,本站會從賣家獲得佣金。在這個過程中,購買者支付的金額(遇到活動折扣時金額會降低↓)並不會因此提高。刊登的價格、折扣與庫存資訊以撰寫當時為準,可能與實際情況不同,購買前請向賣家做最後確認。商品的挑選與評價依本站自訂的標準撰寫,是否支付佣金不會影響介紹順序或評價內容。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

Prove your humanity: 6   +   5   =