awk是什麼
awk是linux環境下的一個指令行工具,但是由于awk強大的能力,我們可以為awk工具傳遞一個字元串,該字元串的内容類似一種程式設計語言的文法,我們可以稱其為Awk語言,而awk工具本身則可以看作是Awk語言的解析器。就好比python解析器與Python語言的關系。我們一般使用awk來做什麼,awk又适合做什麼工作呢。由于awk天生提供對檔案中文本分列進行處理,是以如果一個檔案中的每行都被特定的分隔符(常見的是空格)隔開,我們可以将這個檔案看成是由很多列的文本組成,這樣的檔案最适合用awk進行處理,其實awk在工作中很多時候被用來處理log檔案,進行一些統計工作等。
awk指令的一般組成
awk最常用的工作一般是周遊一個檔案中的每一行,然後分别對檔案的每一行進行處理,一個完整的awk指令形式如下:
awk [options] 'BEGIN{ commands } pattern{ commands } END{ commands }' file
其中options表示awk的可選的指令行選項,其中最常用的恐怕是 -F 它指定将檔案中每一行分隔成列的分隔符号。而緊接着後面的單引号裡面的所有内容是awk的程式腳本,awk需要對檔案每一行分割後的每一列做處理。file則是awk要處理的檔案名稱。讓我們通過demo來體會awk的功能。
awk對每一行進行分割處理
echo '11 22 33 44' | awk '{print $3" "$2" "$1}'
輸出:33 22 11
我們将字元串 11 22 33 44 通過管道傳遞給awk指令,相當于awk處理一個檔案,該檔案的内容就是 11 22 33 44 上面的指令中我們并沒有添加 -F 指定分割符号,實際上預設情況下awk使用空格分割每一行,如果需要指定别的字元則使用-F顯示指定。上面的指令是将 11 22 33 44 通過空格(不管列之間有多少個空格都将當作一個空格處理)分割成4列,在awk中有一種通過
$數字
引用的變量,這種變量引用的内容就是目前行中分割的每一列的内容,數字的序号從1開始,例如$1表示第1列的内容,$2表示第二列,以此類推。$0 表示目前整行的内容。print是awk的内置函數,用于列印出變量的值。 而我們在$3 $2 $1 之間添加了用雙引号引起來的空格,如果沒有,則這些變量的值列印出來會連在一起。這裡的awk指令中{}裡面的内容實際上是我們上面完整模式的中間部分,我們省略了上面的BEGIN塊,END塊,并且中間的程式塊我們也省略掉了pattern部分,也就是如果不添加BEGIN或者END說明那麼該程式塊就是上面完整模式中的中間的那個程式塊,該中間的程式塊所執行的操作就是循環處理檔案内容的每一行,如果檔案有10行,那麼中間的程式塊要運作10次,每一次處理一行的内容,并且處理完目前行之後,下次循環會自動依次處理接下來的行内容。
我們來看看,如果有兩列是什麼效果呢,例如:
echo -e '11 22 33 44\naa bb cc dd' | awk '{print $3" "$2" "$1}'
輸出:
33 22 11
cc bb aa
注意這裡echo指令使用了-e選項的目的就是為了保持字元串中的\n的格式能夠生效,否則該換行将被忽略。那麼上面的指令是如何執行的呢,我們模拟一下awk的執行過程,首先awk讀取第一行的内容,使用空格分隔該行中的列,并将字元串11指派給$1,22指派給$2,33指派給$3,44指派給$4。然後通過print列印出來。接着讀取第二行的内容,同樣執行上面的操作。
使用parttern部分
我們已經學習了awk最簡單的指令,下面我們再加一點東西進去,在程式塊的前面添加pattern部分,例如:
echo -e '1 2 3 4\n5 6 7 8' | awk '$1>2{print $3" "$2" "$1}'
輸出:7 6 5
該程式與上面的程式幾乎一樣,隻不過我們在程式塊前面添加了 $1>2 表示如果目前行的第1列的值大于2則處理目前行,否則不處理。說白了pattern部分是用來從檔案中篩選出需要處理的行進行處理的,如果沒有則循環處理檔案中的所有行。pattern部分可以是任何條件表達式的判斷結果,例如>,<,==,>=,<=,!= 同時還可以使用+,-,*,/運算與條件表達式相結合的複合表達式,邏輯 &&,||,! 同樣也可以使用進來。另外pattern部分還可以使用 /正則/ 選擇需要處理的行。
awk的BEGIN語句塊
BEGIN語句塊是在比對檔案第一行之前運作的語句塊。由于是比對第一行之前運作,實際上在BEGIN語句塊中 $n 是不可用的。一般情況下可以在BEGIN語句塊中做一些變量(awk中可以自定義變量,直接為一個變量指派就定義了一個變量,awk中沒有專門定義變量的關鍵字)初始化的工作,以及一些隻需要在開始僅列印一次的輸出資訊(例如輸出表的表頭)。例如:
echo -e '1 2 3 4\n5 6 7 8' | awk 'BEGIN{print "c1 c2 c3";print ""}{print $3" "$2" "$1}'
輸出為:
c1 c2 c3
3 2 1
7 6 5
注意一個語句塊(花括号包圍)中可以有多條語句,使用分号隔開,這與C語言一樣。如果需要單獨列印空行,需要使用 print "" 我們上面就實作了輸出表頭的效果。
awk的END語句塊
END語句塊是在awk循環執行完所有行的處理之後,才執行的,與BEGIN一樣,END語句塊也隻執行一次,我們看看完整的例子。
echo -e '1\n2\n3' | awk 'BEGIN{print "begin"}{print $1}END{print "end"}'
begin
1
2
3
end
awk定義變量對列求和
test.txt 的内容如下:
11 22 33
23 45 34
22 32 43
awk 'BEGIN{sum=0}{sum+=$1}END{print sum}' test.txt
輸出結果:56
首先在BEGIN語句塊中為變量sum指派0,然後在循環語句塊中将每一行的第1列加到sum中,當檔案的所有行全部循環處理完成之後,列印出sum變量的值。當然這個例子中BEGIN語句塊是可以省略的,我們可以直接在循環語句塊中使用sum變量,此時sum第一次使用,該變量會自動被建立,預設的初始值是0。
awk中的判斷語句
awk的所有語句塊中都可以使用判斷語句,其判斷語句文法與C語言一樣。
//test.txt内容如下
1 2 3
4 5 6
7 8 9
10 11 12
awk '{if($1%2==0)print $1" "$2" "$3}' test.txt
awk中的循環
//while循環
awk 'BEGIN{count=0;while(count<5){print count;count ++;}}'
4
可以看出awk的一個語句塊中可以有比較複雜的複合語句,其使用與C語言幾乎差不多,多條語句之間用分号隔開,複合語句塊用花括号括起來做分隔。
//do..while循環
awk 'BEGIN{count=0;do{print count;count++}while(count<5)}'
//for 循環
awk 'BEGIN{for(count=0;count<5;count++)print count}'
輸出:與上面一樣
可以看到這幾種循環的形式與C語言是一樣的,對于我們了解沒有任何障礙。awk中也使用break退出循環,使用continue跳過本次循環,其含義與C語言一樣。
使用數組分組求和,for..in循環
awk中的數組基本上可以看作是字典,看下面的例子:
//test.txt的檔案内容
zhangsan 2 3
lisi 5 6
zhangsan 8 9
lisi 11 12
wangwu 33 11
将所有第一列相同的分成一個組,并将該組中的第二列求和。
awk '{sum[$1]+=$2}END{for(k in sum)print k" "sum[k]}' test.txt
zhangsan 10
lisi 16
wangwu 33
這個例子裡面使用了for..in循環來周遊數組的key,同時通過key來得到數組的值。對于key不是數字的數組,是不能通過普通的for循環來以數字索引通路數組元素的。我們可以通過length()函數來獲得數組的元素個數,例如length(array)
awk中使用shell變量值
有的時候我們在shell中計算出來的變量值需要被awk指令使用,我們當然不能在awk中直接使用 $VAR,因為美元符号在awk中本來就是特殊符号,在awk中可以使用 $n 引用目前行的第n列的值,是以直接這麼使用是不行的,awk提供了一個選項 -v 來指定變量,在awk中有兩種變量,一種是 $n 形式的變量,這個是在循環檔案的行的時候,用來引用目前行的第n列的值,還有一種變量,不用定義可以直接使用,不需要用美元符号來引用。下面看看shell中的變量值如何在awk中使用:
a=22
b=33
awk -v x=$a -v y=$b 'BEGIN{print x" "y}'
可以看到我們隻需要在使用awk的時候通過 -v 指定awk中将會用到的變量即可,而變量值則可以通過引用shell變量得到,也就是說我們隻能在awk的options部分引用shell的變量,在awk的語句塊中使用美元符号引用變量會被awk解析成自己的變量而不是shell的變量。
awk中的操作符與優先級清單

awk的内置函數
awk定義了很多内置函數,下面我們根據函數類型列出常用的函數,下面的函數隻是一部分,完整的函數清單則需要查閱awk的官方文檔。
算術:
atan2(y,x) 傳回 y/x 的反正切。
cos(x) 傳回 x 的餘弦;x 是弧度。
sin(x) 傳回 x 的正弦;x 是弧度。
exp(x) 傳回 x 幂函數。
log(x) 傳回 x 的自然對數。
sqrt(x) 傳回 x 平方根。
int(x) 傳回 x 的截斷至整數的值。
rand() 傳回任意數字 n,其中 0 <= n < 1。
srand([expr]) 将 rand 函數的種子值設定為 Expr 參數的值,或如果省略 Expr 參數則使用某天的時間。傳回先前的種子值。
字元串:
gsub(reg,str1,str2) 使用str1替換所有str2中符合正規表達式reg的子串
sub(reg,str1,str2) 含義與gsub相同,隻不過gsub是替換所有比對,sub隻替換第一個比對
index(str,substr) 傳回substr在str中第一次出現的索引,注意索引從1開始計算,如果沒有則傳回0
length(str) 傳回str字元串的長度,length函數還可以傳回數組元素的個數
blength(str) 傳回字元串的位元組數
match(str,reg) 與index函數一樣,隻不過reg使用正規表達式,例如match("hello",/lo/)
split(str,array,reg)将str分隔成數組儲存到array中,分隔使用正則reg,或者字元串都可以,傳回數組長度
tolower(str) 轉換為小寫
toupper(str) 轉換為大寫
substr(str,start,length) 截取字元串,從start索引開始的length個字元,如不指定length則截取到末尾,索引從1開始
其他:
system(command) 執行系統指令,傳回退出碼
mktime( YYYY MM dd HH MM ss[ DST]) 生成時間格式
strftime(format,timestamp) 格式化時間輸出,将時間戳轉換為時間字元串
systime() 得到時間戳,傳回從1970年1月1日開始到目前時間(不計閏年)的整秒數
awk的内置變量
awk中同樣定義了很多内置變量,我們可以直接像使用普通變量一樣使用他們,由于awk的版本衆多,有些内置變量并不是得到所有awk版本的支援。
說明:[A][N][P][G]表示支援該變量的工具,[A]=awk、[N]=nawk、[P]=POSIXawk、[G]=gawk
$n 目前記錄的第n個字段,比如n為1表示第一個字段,n為2表示第二個字段。
$0 這個變量包含執行過程中目前行的文本内容。
[N] ARGC 指令行參數的數目。
[G] ARGIND 指令行中目前檔案的位置(從0開始算)。
[N] ARGV 包含指令行參數的數組。
[G] CONVFMT 數字轉換格式(預設值為%.6g)。
[P] ENVIRON 環境變量關聯數組。
[N] ERRNO 最後一個系統錯誤的描述。
[G] FIELDWIDTHS 字段寬度清單(用空格鍵分隔)。
[A] FILENAME 目前輸入檔案的名。
[P] FNR 同NR,但相對于目前檔案。
[A] FS 字段分隔符(預設是任何空格)。
[G] IGNORECASE 如果為真,則進行忽略大小寫的比對。
[A] NF 表示字段數,在執行過程中對應于目前的字段數。
[A] NR 表示記錄數,在執行過程中對應于目前的行号。
[A] OFMT 數字的輸出格式(預設值是%.6g)。
[A] OFS 輸出字段分隔符(預設值是一個空格)。
[A] ORS 輸出記錄分隔符(預設值是一個換行符)。
[A] RS 記錄分隔符(預設是一個換行符)。
[N] RSTART 由match函數所比對的字元串的第一個位置。
[N] RLENGTH 由match函數所比對的字元串的長度。
[N] SUBSEP 數組下标分隔符(預設值是34)。
awk官方文檔:
https://www.gnu.org/software/gawk/manual/gawk.html
您如果覺得寫的還不錯,給個贊呗