我們知道 Linux 三劍客,它們分別是:grep
、sed
、awk
。在前邊已經講過 grep 和 sed,沒看過的同學可以直接點選閱讀,今天要分享的是更為強大的 awk
。
sed 可以實現非互動式的字串替換,grep 能夠實現有效的過濾功能。與兩者相比,awk 是一款強大的文字分析工具,在對資料分析並生成報告時,顯得尤為強悍。
awk 強大的功能,是一般 Linux 命令無法比擬的。在本文中,我不會告訴你 awk 也是一種程式語言,免得會嚇到你。我們只需把它當做 Linux 下一款強大的文字分析工具即可。
這篇文章,我仍然秉持著 實用、實踐 原則,提供大量的示例,但不會面面俱到。通過本文可以幫助你,快速將 awk 運用起來,這些東西足夠應付工作中大多數應用場景。
場景
學習具體使用前,先來看下 awk 能幹些什麼事情:
1. 能夠將給定的文字內容,按照我們期望的格式輸出顯示,列印成報表。
2. 分析處理系統日誌,快速地分析挖掘我們關心的資料,並生成統計資訊;
3. 方便地用來統計資料,比如網站的訪問量,訪問的 IP 量等;
4. 通過各種工具的組合,快速地彙總分析系統的執行資訊,讓你對系統的執行了如指掌;
5. 強大的指令碼語言表達能力,支援迴圈、條件、陣列等語法,助你分析更加複雜的資料;
……
當然 awk 不僅能做這些事情,當你將它的用法融匯貫通時,可以隨心所欲的按照你的意願,來進行高效的資料分析和統計。
不過我們需要知道,awk 不是萬能的,它比較擅長處理格式化的文字,比如 日誌、csv 格式資料等;
原理
我們先來簡單瞭解 awk 基本工作原理,通過下邊的圖文講述,希望你能瞭解 awk 到底是如何工作的。
awk 基本命令格式
結合下圖來詳細說明 awk 工作原理
- 首先,執行關鍵字
BEGIN
標識的{}
中的命令; - 完成
BEGIN
大括號中命令的後,開始執行body
命令; - 逐行讀取資料,預設讀到
\n
分割的內容為一條 記錄,其實就是行的概念; - 將記錄按照指定的分隔符劃分為 欄位,其實就是列的概念;
- 迴圈執行
body
塊中的命令,每讀取一行,執行一次body
,最終完成body
執行; - 最後,執行
END
命令,通常會在END
中輸出最後的結果;
awk 是輸入驅動的,有多少輸入行,就會執行多少次 body
命令。
我們在下邊的示例學習中,要時刻記著:記錄 (Record) 就是行,欄位 (Field) 就是列,BEGIN
是預處理階段,body
是 awk 真正工作的階段,END
是最後處理階段。
實戰 - 入門
從下邊內容開始,我們直接進入到實戰。為了方便舉例,我先把如下資訊儲存到 file.txt
好了,我們先來一個最簡單最常用的 awk 示例,輸出第 1、4、8 列:
大括號裡邊的就是 awk 語句,只能被單引號包含,其中,$1..$N
表示第幾列,$0
表示整個行內容
再來看下 awk
比較實用的功能 格式化輸出。和 C
語言的 printf
格式輸出是一毛一樣,我個人特別喜歡這種格式化方式,而不是 C++
中的流的方式。
%s
表示字串佔位符,-4
表示列寬度為 4
,且左對齊,我們還可以根據需要,列出更復雜的格式來,這裡先不詳細舉例了。
實戰 - 進階
(一)過濾記錄
有些資料可能不是你想要的,可以根據需要進行過濾
上邊的過濾條件為,第 3 列為 root 且第 6 列為 10 的行,才會被輸出。
awk 支援各種比較運算子號 !=
、>
、<
、>=
、<=
,其中 $0
表示整行的所有內容。
(二)內建變數
awk 內建了一些變數,更方便我們對資料的處理
過濾第 3 列為 root 使用者,以及第 2 行內容,且列印時輸出行號。NR
表示當前第幾行,NF
表示當前行有幾列。
(三)指定分隔符
我們的資料,不總是以空格為分隔符,我們可以通過 FS
變數指定分隔符。
我們指定分隔符為 2019
,這樣就將行內容分割為了兩部分,將 2019
替換成了 *
上邊的命令也可以通過 -F
選項指定分割符
如果你需要指定多個分隔符,可以這樣做 -F '[;:]'
。相信聰明的你,一定能夠理解並融會貫通的。
同樣,awk 可以指定輸出時的分隔符,通過 OFS
變數來設定
輸出時,各欄位用 OFS
指定的符號進行了分隔。
實戰 - 高階
(一)條件匹配
列出 root 使用者的所有檔案,以及第一行檔案
上邊匹配第三列中包含 root
的行,~
其實就是正規表示式的匹配。
同樣,awk 可以像 grep 一樣匹配某一行,就像這樣
另外,可以這樣 /Aug|Dec/
匹配多個關鍵詞。
模式取反可以使用 !
符號
(二)拆分檔案
我們來做一件有意思的事情,可以將文字資訊拆分為多個檔案,下邊命令按照月份(第5列)將檔案資訊拆分為多個檔案
awk 支援重定向符號 >
,直接將每行內容重定向到月份命名的檔案了,當然你也可以把指定的列輸出到檔案
(三)if 語句
複雜的條件判斷,可以使用 awk 的 if
語句,awk 的強大正因為它是個指令碼直譯器,擁有一般指令碼語言的程式設計能力,下邊示例通過稍微複雜的條件進行拆分檔案
要注意,if
語句是在大括號裡邊的。
(四)統計
統計當前目錄下,所有 *.c
、*.h
檔案所佔用空間大小總和
第 5 列表示檔案大小,每讀取一行就會將該檔案大小計算到 sum
變數中,在最後 END
階段列印出 sum
,也就是所有檔案的大小總和。
再來看一個例子,統計每個使用者的程式佔用了多少記憶體,注意取值的是 RSS 那一列
這裡用到了 陣列 和 for 迴圈,值得一提的是,awk 的陣列可以理解為字典或 Map
,key 可以是數值和字串,這種資料型別在平時很常用。
(五)字串
通過下邊簡單示例,展示 awk 對字串操作的支援
awk 內建支援一系列的字串函式,length
計算字串長度,toupper
函式轉換字串為大寫。
實戰 - 技巧
為了從整體上理解 awk 工作機制,我們再來看一個綜合的示例,假設有一個學生成績單:
由於此示例程式稍顯複雜,在命令列上不易讀,另外呢,也想通過此案例介紹另外一種 awk 的執行方式,我們的 awk 指令碼如下:
執行 awk 結果如下
我們可以將複雜的 awk 語句寫入指令碼檔案 cal.awk
,然後通過 -f
選項指定從指令碼檔案執行。
- 在
BEGIN
階段,我們初始化了相關變數,並列印了表頭的格式 - 在
body
階段,我們讀取每一行資料,計算該學科和該同學的總成績 - 在
END
階段,我們先列印了表尾的格式,並列印總成績,以及計算了平均值
這個簡單示例,完整的體現了 awk 的工作機制和原理,希望通過此示例能夠幫你真正理解 awk 是如何工作的。
總結歸納
通過上述的示例,我們學習到了 awk 的工作原理,下邊我們來總結下幾個概念和常用的知識點。
(一)內建變數
1. 每一行內容記錄,叫做記錄,英文名稱 Record
2. 每行中通過分隔符隔開的每一列,叫做欄位,英文名稱 Field
明確這幾個概念後,我們來總結幾個重要的內建變數:
NR
:表示當前的行數;NF
:表示當前的列數;RS
:行分隔符,預設是換行;FS
:列分隔符,預設是空格和製表符;OFS
:輸出列分隔符,用於列印時分割欄位,預設為空格ORS
:輸出行分隔符,用於列印時分割記錄,預設為換行符
(二)輸出格式
awk 提供 printf
函式進行格式化輸出功能,具體的使用方式和 C
語法基本一致。
基本用法
常用的格式化方式:
%d
十進位制有符號整數%u
十進位制無符號整數%f
浮點數%s
字串%c
單個字元%e
指數形式的浮點數%x
%X
無符號以十六進位制表示的整數%0
無符號以八進位制表示的整數%g
自動選擇合適的表示法\n
換行符\t
Tab符
(三)程式設計語句
awk 不僅是一個 Linux 命令列工具,它其實是一門指令碼語言,支援程式設計語言所有的控制結構,它支援:
- 條件語句
- 迴圈語句
- 陣列
- 函式
(四)常用函式
awk 內建了大量的有用函式功能,也支援自定義函式,允許你編寫自己的函式來擴充套件內建函式。
這裡只簡單羅列一些比較常用的字串函式:
index(s, t)
返回子串 t 在 s 中的位置length(s)
返回字串 s 的長度split(s, a, sep)
分割字串,並將分割後的各欄位存放在陣列 a 中substr(s, p, n)
根據引數,返回子串tolower(s)
將字串轉換為小寫toupper(s)
將字串轉換為大寫
這裡只簡單總結一些常用的字串功能函式,具體使用方法,還需要你參照前邊的示例程式,舉一反三,運用到實際問題中。
本次分享就到這裡了,謝謝大家的閱讀,我是肖邦。關注我的公眾號「程式設計修養」,大量的乾貨文章等你來!
公眾號後臺回覆「1024」有驚喜!
推薦閱讀: