前言
由於最近的工作內容的關係, 經常需要對文本文件做一些處理. 每次都要寫個腳本來處理實在是有點麻煩. 這時候想起來很久以前稍微接觸過的AWK, 來做這個工作真的是再合適不過了.
因此, 趁着這個機會, 把AWK深入學習一點,記錄在此.
概述
AWK是一門解釋型的編程語言。在文本處理領域它是非常強大的,它的名字來源於它的三位作者的姓氏:Alfred Aho, Peter Weinberger 和 Brian Kernighan。
GNU/Linux發佈的AWK目前由自由軟件基金會(FSF)進行開發和維護,通常也稱它爲 GNU AWK。
awk在下列任務中都有非常不錯的發揮, 本文後續也會舉很多示例.
- 文本處理
- 輸出格式化的文本報表
- 執行算數運算
- 執行字符串操作等等
簡單來說awk就是把文件逐行的讀入,以空格爲默認分隔符將每行切片,切開的部分再進行各種分析處理。
你可以將其理解爲一個linux命令,只是使用參數以及方法多樣一些. 因爲我們經常會在命令行直接使用它.
你也可以將其理解爲一個簡易的腳本語言, 因爲在使用過程中我們也可以寫邏輯表達式等各種語句.
先來個熱場的示例.
已有一個文本文件,格式如下:
前面的數字是熱度, 後面的字符串是搜索詞.
100 阿里巴巴
200 京東
300 淘寶
400 awk怎麼使用
我們想計算, 熱度大於等300的詞的熱度, 在總熱度中的一個比例
雖然聽起來有點繞, 但是這是一個非常常見的需求, 對應到這個示例中我們是想計算 (300 + 400 ) / ( 100 + 200 + 300 + 400), 此時我們只能打開我們的編譯器, 選擇一門語言之後開始寫代碼了. 其實不用.
把上面的文本內容放進 a.txt中,然後執行下面的shell命令即可, 你會看到預期之中的 0.7.
awk '{{if ($1 >= 300) {sub_sum +=$1}};sum += $1 }; END{ret = sub_sum * 1.0 / sum; print ret}' a.txt
這段腳本做了什麼事情呢?
- 遍歷每一行,按照空格爲分隔符切割.
- 維護兩個變量, 每一行都將第一列的數字累加到sum. 如果數字大於等於300,則將他累加到 sub_sum.
- 在執行結束後, 將 sub_sum和sum做一個除法.
怎麼樣, 是不是比寫其他shell或者python腳本快多了?
接下來將進入學習時間, 我們逐個知識點的學習,看完本文,你也能這麼花裏胡哨的解決文本處理問題~.
基礎語法
腳本語法
awk除了可以在命令行執行之外, 還可以寫成腳本文件進行執行.我們大致的瞭解一下這個用法,之後不做詳細講解,以命令行用法爲主要內容. 因爲文本的用戶和命令行大同小異.
首先,創建一個包含腳本內容的文本文件 test.awk
{print $1 }
然後我們用命令行執行這個腳本文件.
awk -f command.awk marks.txt
這個其實是相當於命令行直接執行的一個擴展, 當你寫的腳本十分複雜(不推薦),且需要多人合作或者共享的時候, 腳本文件會是一個不錯的選擇.
命令行語法
awk [options] file ...
把上面腳本文件語法中的內容寫到 options 即可.
上面所講的, 是 awk是什麼以及 怎麼在系統中使用awk,接下來的內容就是 awk自身的一些語法.
程序結構
awk程序的思路是, 逐行處理一個文件.
那麼讓我們想一下, 當我們想要 處理一個文件的時候會需要做些什麼?
- 進行處理之前, 先初始化一些信息.
- 逐行處理文本, 記錄一些信息.
- 處理完之後,進行一些信息整理.比如打印,重定向等.
awk的程序結構也是如此.
BEGIN 語句塊
BEGIN {awk-commands}
BEGIN語句塊在程序開始的使用執行,它只執行一次,在這裏可以初始化變量。BEGIN是AWK的關鍵字,因此它必須爲大寫,注意,這個語句塊是可選的。
BODY 語句塊
/pattern/ {awk-commands}
BODY語句塊中的命令會對輸入的每一行執行,我們也可以通過提供模式來控制這種行爲。注意,BODY語句塊沒有關鍵字。
END 語句塊
END {awk-commands}
END語句塊在程序的最後執行,END是AWK的關鍵字,因此必須爲大寫,它也是可選的。
所以一個添加了全部可選項的awk命令如下所示:
awk [options] 'BEGIN{};{};END{}' file.txt
操作符
awk對常用的操作符都有支持,且與c語言使用方法一樣.具體支持的操作符有:
- 算數操作符
- 增減運算符
- 自增自減操作符
- 賦值操作符
- 關係操作符
- 邏輯操作符
- 三元操作符
- 一元操作符
- 指數操作符
- 字符串連接操作符
- 正則表達式操作符
流程控制
awk支持流程控制, 比如在本文最前方的示例中我們使用了if語句.
或者類似下面的if語句都是合法的.
awk 'BEGIN {
a = 30;
if (a==10)
print "a = 10";
else if (a == 20)
print "a = 20";
else if (a == 30)
print "a = 30";
}'
循環
循環操作與其他C系語言一樣,主要包括 for,whlie,do…while,break,continue 語句.
示例:
awk 'BEGIN {
sum = 0; for (i = 0; i < 20; ++i) {
sum += i; if (sum > 50) exit(10); else print "Sum =", sum
}
}
內建變量
- 0 表示正在處理的當前行
- 1 表示當前行的第一列, 以此類推, 2表示第二列…
- NR 表示文件中的行號,表示當前是第幾行
- NF 表示文件中的當前行被分割的列數,可以理解爲 MySQL 數據表裏面每一條記錄有多少個字段,所以 NF 表示倒數第一格字段, (NF-1) 表示倒數第二個字段.
- FS 表示 awk 的輸入分隔符,默認分隔符爲空格和製表符,可以對其進行自定義設置
- OFS 表示 awk 的輸出分隔符,默認爲空格,也可以對其進行自定義設置
- FILENAME 表示當前文件的文件名稱,如果同時處理多個文件,它也表示當前文件名稱
- RS 行分隔符,用於分割行,默認爲換行符
- ORS 輸出記錄的分隔符,默認爲換行符
內建函數
與內建變量相對應的, 也有一部分的內建函數.
awk 還提供了一些內置函數,比如:
- toupper() 用於將字符轉爲大寫
- tolower() 將字符轉爲小寫
- length() 長度
- substr() 子字符串
- sin() 正弦
- cos() 餘弦
- sqrt() 平方根
- rand() 隨機數
內建函數還有一些其他的, 具體可以在使用時在 man awk
中查詢.
自定義函數
雖然我個人是不支持用awk來做這麼繁雜的編程工作的,但是awk支持我們自定義函數並且調用. 語法規範如下:
function function_name(argument1, argument2, ...) {
function body
}
我們可以在一個awk腳本中放入一下內容, 然後執行它.
function main(){
print "function"
}
BEGIN {
main()
}
常用場景示例
這裏會列出一些常用的,簡單的使用示例.
所有的示例都以下面的示例爲輸入進行運行.
1) Amit Physics 80
2) Rahul Maths 90
3) Shyam Biology 87
4) Kedar English 85
5) Hari History 89
打印某列或者字段
AWK可以只打印輸入字段中的某些列。
$ awk '{print $3 "\t" $4}' marks.txt
Physics 80
Maths 90
Biology 87
English 85
History 89
在示例文本中,第三列包含了科目名,第四列則是得分,上面的例子中,我們只打印出了這兩列,$3 和 $4 代表了輸入記錄中的第三和第四個字段。
打印所有的行
默認情況下,AWK會打印出所有匹配模式的行
$ awk '/a/ {print $0}' marks.txt
2) Rahul Maths 90
3) Shyam Biology 87
4) Kedar English 85
5) Hari History 89
上述命令會判斷每一行中是否包含a,如果包含則打印該行,如果BODY部分缺失則默認會執行打印,因此,上述命令和下面這個是等價的
$ awk '/a/' marks.txt
打印匹配模式的列
當模式匹配成功時,默認情況下AWK會打印該行,但是也可以讓它只打印指定的字段。例如,下面的例子中,只會打印出匹配模式的第三和第四個字段。
$ awk '/a/ {print $3 "\t" $4}' marks.txt
Maths 90
Biology 87
English 85
History 89
任意順序打印列
$ awk '/a/ {print $4 "\t" $3}' marks.txt
90 Maths
87 Biology
85 English
89 History
統計匹配模式的行數
$ awk '/a/{++cnt} END {print "Count = ", cnt}' marks.txt
Count = 4
打印超過18個字符的行
$ awk 'length($0) > 18' marks.txt
3) Shyam Biology 87
4) Kedar English 85
查找history歷史中,最常用的10個命令
history | awk '{a[$2]++}END{for(i in a){print a[i] " " i}}' | sort -rn | head
過濾文件中重複行
awk '!x[$0]++' <file>
將一行長度超過 72 字符的行打印
awk 'length>72' file
查看最近哪些用戶使用系統
last | grep -v "^$" | awk '{ print $1 }' | sort -nr | uniq -c
計算文本中的數值的和
awk '{s+=$1} ENG {printf "%.0f", s}' /path/to/file
快速幫助
當你用的時候臨時有忘記的或者不確定的,隨時可以查看幫助命令.
man awk
參考文章
完。
ChangeLog
2019-12-05 完成以上皆爲個人所思所得,如有錯誤歡迎評論區指正。
歡迎轉載,煩請署名並保留原文鏈接。
聯繫郵箱:[email protected]
更多學習筆記見個人博客或關注微信公衆號 <呼延十 >------>呼延十