天天看點

linux awk 一看就懂

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中的操作符與優先級清單

linux 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

您如果覺得寫的還不錯,給個贊呗