awk 指令教學

Last updated on  Dec 31, 2022  in  Unix-like 命令列教學  by  Amo Chen  ‐ 3 min read

很多 UNIX-like(Unix, Linux) 程式都是以欄位表格作為執行結果報告,有些時候資料量更可能動輒上百、上千、上萬筆,因此對於 UNIX-like 的使用者而言,如何有效的從這些欄位表格的報告中擷取所需的資料就顯得相當重要,畢竟一行一行細看太花時間成本,也相對沒有效率。

為了幫助我們快速完成資料擷取的工作,在很多 Unix-like 系統中,都有相關指令可以幫忙,例如 cut, grep, sed, awk 等等,其中最值得再進一步深入學習的,大概就是 awk 了。

本篇教學會詳細說明常見的 awk 用法及可能的應用,以使各位對使用 awk 都能有基礎的認識。

取得欄位值

awk 指令在讀取檔案或資料流時,以一行為單位,一次單以一行進行處理,預設的欄位以空白或Tab(\t)進行分隔,分隔完的欄位值從左邊第一欄為 $1 開始,往右遞增累加。在使用 awk 時,除了直接讀取檔案之外,也可以利用管線(pipe, |)將資料流導向給 awk 進行處理。

p.s. $0 代表未經分隔的完整一行, NF (保留字,無 $ )代表全部欄位數

例如:將 ls -al 結果簡化成檔案名稱對應檔案擁有者

$ ls -al | awk '{print $9"\t=>\t"$3}'
. => user
.. => user
.bash_history => user
.bashrc => user
file.txt => user

BEGIN, END 的用法

上述取得欄位值的例子,也可以利用 BEGIN 關鍵字幫 awk 產出的結果在第一行加上一些說明,更可以使用 END 幫產生的結果加上一些註釋。 BEGIN 在 awk 中所指就是「讀取資料之前」, END 則是代表「讀取完資料之後」。

例如:

$ ls -al | awk 'BEGIN {print "FILE\t=>\tOWNER"} {print $9"\t=>\t"$3} END {print "DONE!\n"}'
FILE => OWNER
. => user
.. => user
awkex1.txt => user
.bash_history => user
.bashrc => user
DONE!

以上的例子就代表「讀取資料之前列印出 FILE => OWNER 」,「資料讀取完畢之後列印出 DONE! 」

if-else

awk 也可以加入 if-else 的條件判斷。例如以下檔案內容:

// Salary.txt
A 19000
B 22000
C 10500
D 65000
$ awk '{if($2 > 22000) {print $1" is rich!"} else {print $1" is poor..."}}' Salary.txt
A is poor...
B is poor...
C is poor...
D is rich!

變數

既然都已經有 if-else 了,當然變數的運用也是跑不掉的!在 awk 中 $(數字) 代表 awk 所讀取當前資料行內的欄位變數,因此其他跟欄位無關的變數,是不需要加錢號(dollar sign, $ )。 繼 if-else 的例子之後,同樣的檔案內容,我們希望統計出薪水低於某些數字以下的人的薪水總和,此時就可以運用變數來進行加總。

$ awk 'BEGIN{SUM=0;} {if($2 <= 22000) SUM+=$2} END {print "Total is ... "SUM}' Salary.txt
Total is ... 51500

上述範例,在 BEGIN 區塊內為變數 SUM 設定初始值,讀取每一行時,進行判斷是否薪水大於 22000,如果大於 22000 則進行累加,讀取完所有資料之後, END 區塊會將變數 SUM 列印出來。

正規表示式

功能強大的 awk 也可以加上正規表示式輔助過濾資料。

基本式 awk '/pattern1/ {action1} /pattern2/ {action2} ... ' 以下的範例列出 B 開頭的資料列:

$ awk '/^B.*\s[0-9]*/ {print $0}' Salary.txt

更改欄位分隔依據(field separator)

分隔 CSV 檔案時,所需用到的分隔依據就不再是空白或Tab(\t)了,此時可以使用參數 -F 更改欄位分隔符號。

例如:

$ awk -F ',' '{print $1}' csv_file.txt

取出特定範圍的所有行

對於日誌型的資料,通常會有時間戳記,而且資料會按照順序排列,如果要取出某區間的資料也可以使用 awk 。

例如以下檔案:

# test.txt
a1
1
2
3
4
b1
5
6
7
8
c1
9
10
11
a1
12
13
b1

我們想取出之間的所有行,可以使用以下指令:

$ awk '/a1/, /b1/' test.txt
a1
1
2
3
4
b1
a1
12
13
b1

以上是常被使用的幾種 awk 用法。如果需要更詳細的使用方法,在 man page 中就有很多相關的說明。

參考資料

http://www.grymoire.com/Unix/Awk.html

對抗久坐職業傷害

研究指出每天增加 2 小時坐著的時間,會增加大腸癌、心臟疾病、肺癌的風險,也造成肩頸、腰背疼痛等常見問題。

然而對抗這些問題,卻只需要工作時定期休息跟伸展身體即可!

你想輕鬆改變現狀嗎?試試看我們的 PomodoRoll 番茄鐘吧! PomodoRoll 番茄鐘會根據你所設定的專注時間,定期建議你 1 項辦公族適用的伸展運動,幫助你打敗久坐所帶來的傷害!

贊助我們的創作

看完這篇文章了嗎? 休息一下,喝杯咖啡吧!

如果你覺得 MyApollo 有讓你獲得實用的資訊,希望能看到更多的技術分享,邀請你贊助我們一杯咖啡,讓我們有更多的動力與精力繼續提供高品質的文章,感謝你的支持!