在Linux世界里,文本處理是運維、開發繞不開的日常——從分析日志、提取配置信息到統計數據,都需要高效的工具支撐。而awk,作為一款強大的文本分析語言,憑借“按字段處理”的核心能力,成為了比grep(單純匹配)、sed(整行編輯)更靈活的“文本處理瑞士軍刀”。今天這篇文章,我們從原理到實戰,帶你徹底掌握awk的用法,解決90%的文本處理場景。
一、認識awk:它是什么,從哪里來?
awk并非簡單的命令,而是一門專為文本處理設計的編程語言,誕生于20世紀70年代的貝爾實驗室,名字取自三位創始人Alfred Aho、Peter Weinberger、Brian Kernighan的姓氏首字母。
1.1 awk的版本區別
在Linux系統中,我們實際使用的awk大多是GNU awk(gawk)——因為所有GNU/Linux發行版(如CentOS、Ubuntu)都自帶gawk,且它完全兼容早期的AWK(AT&T原版)和NAWK(New AWK,原版升級版)。
你可以通過以下命令驗證:
# 查看awk的實際路徑
which awk # 輸出:/usr/bin/awk
# 查看是否為gawk的軟鏈接
ll `which awk` # 輸出:lrwxrwxrwx. 1 root root 4 ... /usr/bin/awk -> gawk
簡單說:在Linux中輸入awk命令,實際執行的是gawk。
二、awk的核心:工作原理與流程
要用好awk,必須先理解它的“做事邏輯”——這也是它和sed最大的區別:
- sed:以“整行”為單位處理文本;
- awk:先將每行拆分為“字段”(默認用空格/制表符分隔),再按字段處理,支持邏輯判斷、數學運算。
2.1 工作原理
awk的處理流程可以概括為“逐行讀取→字段拆分→條件匹配→執行操作”:
- 從標準輸入(如管道)或文件中逐行讀取文本;
- 按默認分隔符(空格/制表符)或自定義分隔符,將當前行拆分為多個“字段”,用
$1
(第一列)、$2
(第二列)…$n
(第n列)表示,$0
表示整行; - 根據指定的“模式”(如包含某個關鍵詞、行號范圍)判斷是否處理當前行;
- 若匹配模式,執行指定的“動作”(如打印字段、統計計數);
- 重復1-4,直到文件讀取完畢。
2.2 三大核心模塊:BEGIN → 主體 → END
awk的腳本結構由三個可選模塊組成,執行順序嚴格固定,這是它的靈魂:
模塊 | 執行時機 | 作用示例 |
---|---|---|
BEGIN | 讀取文本前執行(僅1次) | 初始化變量、打印表頭 |
主體 | 逐行讀取文本時執行(每行會觸發) | 字段提取、條件判斷 |
END | 讀取文本后執行(僅1次) | 匯總統計結果、打印最終值 |
用流程圖理解更直觀:
開始 → 執行BEGIN模塊 → 讀取一行文本 → 執行主體模塊(按模式處理) → 文件是否結束?↑ ↓└──────────────────────┘(否)↓(是)執行END模塊 → 結束
三、awk基礎:語法與核心內置變量
掌握語法和內置變量,是玩轉awk的第一步。
3.1 基本語法
awk有兩種常用命令格式,根據場景選擇:
格式1:直接在命令行寫邏輯
awk [選項] '模式{動作}' 文件名1 文件名2...
- 選項:常用
-F
指定字段分隔符(如-F:
表示用冒號分隔); - 模式:可選,如行號(
NR==5
)、關鍵詞匹配(/root/
); - 動作:必須用
{}
包裹,如print $1
(打印第一列)、變量運算。
格式2:用腳本文件(適合復雜邏輯)
當邏輯復雜時,將模式和動作寫入腳本文件,用-f
指定:
awk -f 腳本文件 文件名
3.2 必學內置變量
awk預定義了一批“開箱即用”的變量,覆蓋90%的實戰場景,務必牢記:
內置變量 | 含義說明 | 實戰常用場景 |
---|---|---|
FS | 輸入字段分隔符(默認:空格/制表符) | BEGIN{FS=“:”}(用冒號分隔) |
NF | 當前行的字段總數(列數) | 打印最后一列:print $NF |
NR | 當前處理的行號(所有文件統一計數) | 打印前5行:NR<=5 |
FNR | 當前處理的行號(每個文件單獨計數) | 多文件對比時用 |
$0 | 當前處理的整行內容 | 打印整行:print $0 |
$n | 當前行的第n個字段(n為數字,如$1是第一列) | 提取用戶名:print $1 |
OFS | 輸出字段分隔符(默認:空格) | 輸出用“—”分隔:BEGIN{OFS=“—”} |
FILENAME | 當前處理的文件名 | 多文件處理時標記來源 |
四、awk實戰:從基礎到生產級案例
理論講完,直接上案例——這些場景都是Linux運維/開發的高頻需求,跟著練一遍就能上手。
4.1 基礎案例:字段提取與格式控制
案例1:提取/etc/passwd的關鍵信息
/etc/passwd
用冒號分隔,包含用戶名、UID、家目錄等信息,用awk輕松提取:
# 1. 用冒號分隔,打印用戶名($1)和家目錄($6),輸出用制表符分隔
awk -F: '{print $1 "\t" $6}' /etc/passwd
# 輸出示例:
# root /root
# bin /bin
# 2. 只打印包含“root”的行,且顯示行號(NR)
awk -F: '/root/{print NR, $1, $6}' /etc/passwd
# 輸出示例:
# 1 root /root
# 10 operator /root# 3. 打印最后一列(登錄Shell),并標注“行號-列數”
awk -F: '{print "第" NR "行,共" NF "列,Shell:" $NF}' /etc/passwd
案例2:自定義多分隔符
如果文本用多種符號分隔(如:
和/
),可在-F
后用[]
指定:
# 用“:”或“/”分隔,打印第9列(適用于/etc/passwd的Shell路徑提取)
awk -F[:/] '{print $9}' /etc/passwd
# 輸出示例:
# bash
# nologin
4.2 進階案例:條件判斷與統計
案例1:數值與字符串比較
awk支持數值比較(==
、<
、>
、>=
)和字符串精確匹配(需加引號):
# 1. 打印UID=0的用戶(管理員用戶,$3是UID)
awk -F: '$3==0{print $1 "是管理員"}' /etc/passwd
# 輸出:root是管理員# 2. 打印UID>=1000的普通用戶($3>=1000)
awk -F: '$3>=1000{print $1 "是普通用戶,UID:" $3}' /etc/passwd# 3. 精確匹配用戶名“root”(字符串必須加雙引號!)
awk -F: '$1=="root"{print $0}' /etc/passwd
案例2:模糊匹配(~ 和 !~)
用~
表示“包含”,!~
表示“不包含”,結合正則表達式使用:
# 1. 打印Shell包含“bash”的用戶(即能登錄的用戶)
awk -F: '$7~"bash"{print $1}' /etc/passwd# 2. 打印Shell不包含“nologin”且不包含“bash”的用戶
awk -F: '$7!~"nologin" && $7!~"bash"{print $1}' /etc/passwd
案例3:用BEGIN/END做統計匯總
BEGIN初始化變量,END輸出最終結果,適合統計場景:
# 1. 統計/etc/passwd的總行數(END中用NR,因為NR是總計數)
awk 'END{print "/etc/passwd共有" NR "行"}' /etc/passwd# 2. 統計能登錄的用戶數(Shell為bash)
awk -F: 'BEGIN{count=0} $7~"bash"{count++} END{print "能登錄的用戶數:" count}' /etc/passwd# 3. 計算1-10的總和(BEGIN中用循環)
awk 'BEGIN{sum=0; for(i=1;i<=10;i++){sum+=i}; print "1-10總和:" sum}'
4.3 生產級案例:系統監控與日志分析
awk常與其他命令結合,實現系統監控和日志統計,這是運維的核心技能。
案例1:查看內存使用率
用free -m
獲取內存信息,awk計算使用率:
# 計算內存使用率(已用/總內存 *100,保留整數)
free -m | awk '/Mem:/ {used=$3; total=$2; rate=int(used/total*100); print "內存使用率:" rate "%"}'
# 輸出:內存使用率:35%
4.4 擴展生產案列:網卡的ip、流量
[root@localhost ~]# ifconfig ens33 | awk '/netmask/{print "本機的ip地址是"$2}'
[root@localhost ~]# ifconfig ens33 | awk '/RX p/{print $5"字節"}'
[root@localhost ~]# df -h | awk 'NR==2{print $4}'
4.5 邏輯運算
[root@localhost ~]# awk -F: '$3<10 || $3>=1000' /etc/passwd
[root@localhost ~]# awk -F: '$3>10 && $3<1000' /etc/passwd
[root@localhost ~]# awk -F: 'NR>4 && NR<10' /etc/passwd
4.6 其他內置變量
其他內置變量的用法FS(輸入)、OFS、NR、FNR、RS、ORSFS:輸入字段的分隔符 默認是空格OFS:輸出字段的分隔符 默認也是空格
FNR:讀取文件的記錄數(行號),從1開始,新的文件重新重1開始計數
RS:輸入行分隔符 默認為換行符
ORS:輸出行分隔符 默認也是為換行符
五、高級用法:邏輯控制與數組
5.1 awk高級用法 if語句
awk的if語句與其他編程語言類似,支持單分支、雙分支兩種常見形式,可根據字段內容或行號等條件執行不同操作,適用于需要條件篩選的文本處理場景。
- 單分支if語句:僅當條件成立時執行動作,常用于篩選符合條件的行。
示例:篩選/etc/passwd
中第三列(UID)小于10的行(系統用戶)并打印整行,命令如下:`[root@localhost ~]# awk -F: '{if($3<10){print $0}}' /etc/passwd`。
- 雙分支if-else語句:條件成立時執行一個動作,不成立時執行另一個動作,適用于二選一的處理場景。
示例:處理/etc/passwd
時,若第三列(UID)小于10則打印第三列,否則打印第一列(用戶名),命令如下:
[root@localhost ~]# awk -F: '{if($3<10){print $3}else{print $1}}' /etc/passwd
。
5.2 擴展 AWK 數組
awk的數組具有靈活的下標特性,不僅支持數字下標,還支持字符串下標(字符串需用雙引號包裹),且無需預先定義數組長度,可動態存儲數據,常用于統計、去重等場景。
數組核心特點與基礎示例
- 下標類型:數字下標用于有序數據存儲,字符串下標用于關聯數據(如“鍵-值”對)存儲。
- BEGIN模塊特性:BEGIN中的命令僅執行一次,適合數組初始化操作。
基礎示例:
-
數字下標數組:初始化數組
a
,下標為0和1,分別賦值10和20,打印指定下標值:
[root@localhost ~]# awk 'BEGIN{a[0]=10;a[1]=20;print a[1]}'
(輸出20)
[root@localhost ~]# awk 'BEGIN{a[0]=10;a[1]=20;print a[0]}'
(輸出10)。
-
字符串下標數組:初始化數組
a
,下標為字符串“abc”和“xyz”,分別賦值10、20或字符串,打印指定下標值:
[root@localhost ~]# awk 'BEGIN{a["abc"]=10;a["xyz"]=20;print a["abc"]}'
(輸出10)
[root@localhost ~]# awk 'BEGIN{a["abc"]="aabbcc";a["xyz"]="xxyyzz";print a["xyz"]}'
(輸出xxyyzz)。
5.3 擴展awk 循環
awk支持for循環,常與數組結合使用,實現數組遍歷、數據迭代等操作,尤其適合批量處理數組中的統計數據。
循環與數組結合的生產案例
示例:初始化數組a
(下標0-2,值分別為10、20、30),通過for循環遍歷數組,打印每個下標的“下標-值”對:
[root@localhost ~]# awk 'BEGIN{a[0]=10;a[1]=20;a[2]=30;for(i in a){print i,a[i]}}'
。
生產場景:基于數組與循環的日志統計
場景1:統計httpd訪問日志中客戶端IP的出現次數
通過數組存儲IP(下標為IP地址),每遇到一個IP就使對應數組值+1(計數),最后用for循環遍歷數組輸出統計結果,命令如下:
[root@localhost ~]# awk '{a[$1]+=1;} END {for(i in a){print a[i]" "i;}}' /var/log/httpd/access_log | sort -r
[root@localhost ~]# awk '{ip[$1]++;}END{for(i in ip){print ip[i],i}}' /var/log/httpd/access_log | sort -r
。
原理:數組ip
的下標為日志第一列(客戶端IP),ip[$1]++
實現IP出現次數累加;END模塊中for循環遍歷數組,sort -r
按次數倒序排列結果。
場景2:統計/var/log/secure
中失敗登錄的IP及次數
篩選日志中包含“Failed password”(失敗登錄)的行,用數組ip
存儲IP(下標為日志第11列,即客戶端IP)并計數,最后遍歷數組輸出,命令如下:
[root@localhost ~]# awk '/Failed password/{ip[$11]++}END{for(i in ip){print i","ip[i]}}' /var/log/secure
。
5.4 生產腳本編寫:數組與邏輯控制結合
基于數組統計的結果,結合shell的if邏輯和循環,可編寫自動化腳本實現告警等功能。例如,篩選/var/log/secure
中失敗登錄次數≥3次的IP并提示告警,腳本內容如下:
#!/bin/ bash
x=`awk '/Failed password/{ip[$11]++}END{for(i in ip){print i","ip[i]}}' /var/log/secure`
#190.168.10.22 3
for j in $x
do
ip=`echo $j | awk -F "," '{print $1}'`
num=`echo $j | awk -F "," '{print $2}'`
if [ $num -ge 3 ];then
echo "警告! $ip訪問本機失敗了$num次,請速速處理!"
fi
done
原理:先用awk統計失敗登錄IP及次數(結果格式為“IP,次數”),再通過shell循環拆分IP和次數,若次數≥3則打印告警信息。
拓展:當一個ip三次登錄失敗時禁止其繼續連接。
iptables -A INPUT -p tcp --dport 22 -s $ip -j DROP
六、awk vs grep vs sed:該用誰?
很多人分不清這三個工具,最后總結一張表,幫你快速選擇:
工具 | 核心能力 | 適用場景 | 一句話總結 |
---|---|---|---|
grep | 文本搜索與匹配 | 單純查找關鍵詞、過濾行 | “找東西” |
sed | 流式編輯(整行處理) | 替換文本、刪除行、插入行 | “改東西” |
awk | 字段處理、統計與格式化 | 提取列、計算數據、生成報告 | “拆字段、算數據” |
七、學習建議
awk的語法靈活,光看理論不夠,建議:
- 先練基礎案例:用
/etc/passwd
、/var/log/messages
等系統文件練手,熟悉內置變量; - 再做實戰場景:嘗試統計自己項目的日志(如用戶訪問次數、錯誤碼統計);
- 復雜邏輯寫腳本:當命令行邏輯過長時,用
-f
腳本文件,方便維護。
掌握awk后,你會發現以前需要寫幾行Python的文本處理需求,用一行awk就能搞定——這就是Linux命令行的效率之美!
需要我幫你把某類awk案例(比如日志統計、系統監控)整理成可直接復用的腳本嗎?