一、概述
awk的名稱來源於其最初設計者Alfred V. Aho, Peter J. Weinberger, and Brian W. Kernighan的姓氏。awk最原始的版本是1977年在AT&T貝爾實驗室誕生的,awk經過改進生成的新的版本nawk,gawk,現在預設linux系統下日常使用的是gawk。我目前使用的作業系統為CentOS Linux release 7.2.1511,系統自帶的awk版本為:GNU Awk 4.0.2。
二、awk的用途
- 文字處理。
- 格式化輸出文字、報告。
- 數字運算(在我之前的文章中,探討過整數的算術運算,浮點運算可以使用awk)。
- 執行字串操作。
三、系統內幾個有關awk命令的區別與聯絡
通過命令,我們可以看到:
[awk@GeekDevOps ~]$ ls -l /bin/*awk
lrwxrwxrwx. 1 root root 4 11月 20 10:41 /bin/awk -> gawk
-rwxr-xr-x. 1 root root 514136 6月 10 2014 /bin/dgawk
-rwxr-xr-x. 1 root root 428576 6月 10 2014 /bin/gawk
-rwxr-xr-x. 1 root root 3188 6月 10 2014 /bin/igawk
-rwxr-xr-x. 1 root root 428600 6月 10 2014 /bin/pgawk複製程式碼
與awk有關的命令竟然有這麼多!在用whatis命令看一下這些都是些什麼鬼:
[awk@GeekDevOps ~]$ whatis awk gawk dgawk pgawk igawk
awk (1) - pattern scanning and processing language
gawk (1) - pattern scanning and processing language
dgawk (1) - pattern scanning and processing language
pgawk (1) - pattern scanning and processing language
igawk (1) - gawk with include files複製程式碼
不太確信以上結果,我又使用man、info挨個看了一下,結果awk、gawk、dgawk、pgawk都是gawk,線上手冊一模一樣。唯獨igawk與前面四個不太一樣,是包含檔案的gawk,其實也是gawk。awk是gawk的一個軟連線,大家都是gawk!
四、awk的使用
4.1 理解awk的工作原理
Read
awk從輸入流(檔案,管道或者標準輸入)中讀取一行,然後儲存到記憶體中。
Execute
所有的AWK命令都依次在輸入文字上執行。預設情況下,awk會對每一行執行命令,可以通過提供模式限制這種行為。
Repeat
處理過程不斷重複,從首行開始直到到達檔案結尾。
建立marks.txt檔案備用:
[awk@GeekDevOps ~]$ echo "1) Amit Physics 80
> 2) Rahul Maths 90
> 3) Shyam Biology 87
> 4) Kedar English 85
> 5) Hari History 89">marks.txt複製程式碼
4.2 awk的結構
BEGIN 語句塊
BEGIN語句塊的語法:
BEGIN {awk-commands}複製程式碼
BEGIN語句塊在程式開始的使用執行,只執行一次,在這裡可以初始化變數。BEGIN是AWK的關鍵字,因此它必須為大寫。注意,這個語句塊是可選的。
BODY 語句塊
BODY語句塊的語法:
/pattern/ {awk-commands}複製程式碼
BODY語句塊中的命令會對輸入的每一行執行,也可以通過提供模式來控制這種行為。注意,BODY語句塊沒有關鍵字。
END 語句塊
END語句塊的語法:
END {awk-commands}複製程式碼
END語句塊在程式的最後執行,END是AWK的關鍵字,因此必須為大寫,它也是可選的。
結合以上2點,舉個例子來理解一下:
[awk@GeekDevOps ~]$ awk BEGIN'{printf "Sr NO\tName\tSub\tMarks\n"} {print} END{print "Done!"}' marks.txt
Sr NO Name Sub Marks
1) Amit Physics 80
2) Rahul Maths 90
3) Shyam Biology 87
4) Kedar English 85
5) Hari History 89
Done!
[awk@GeekDevOps ~]$ awk BEGIN'{printf "Sr NO\tName\tSub\tMarks\n"} END{print "Done!"}' marks.txt
Sr NO Name Sub Marks
Done!複製程式碼
在本例中,BEGIN塊被執行一次,給文字內容增加了一行標題。body塊從文字第一行掃描直至檔案末尾。END塊也僅僅執行了一次。
4.3 使用awk
通常情況下,awk命令較簡短時我們直接按照以下方式執行awk命令:
awk 'program' input-file1 input-file2 …複製程式碼
如果awk內容較多的話,我們以以下格式來執行awk命令(此處program-file為awk指令碼內容):
awk -f program-file input-file1 input-file2 …複製程式碼
awk是一門解釋型的語言,所以也可以像執行bash shell一樣執行awk指令碼:
#!/bin/awk -f
BEGIN{print "My name is Ivan Du!"}複製程式碼
[awk@GeekDevOps ~]$ chmod u+x GeekDevOps.awk
[awk@GeekDevOps ~]$ ./GeekDevOps.awk
My name is Ivan Du!複製程式碼
4.4 列印某列或某欄位
[awk@GeekDevOps ~]$ cat best.txt
www CC ICBC
[awk@GeekDevOps ~]$ awk '{print $1}' best.txt
www
[awk@GeekDevOps ~]$ awk '{print $3}' best.txt
ICBC
[awk@GeekDevOps ~]$ awk -F "\t" '{print $3}' best.txt
ICBC複製程式碼
預設情況下,awk以空格、製表符等符號為分隔符。從每一行的第一個字串開始掃描,第一個字串為
4.5 列印匹配模式的列
當模式匹配成功時,預設情況下awk會列印該行,但是也可以讓它只列印指定的欄位。例如,下面的例子中,只會列印出匹配模式的第三和第四個欄位。
[awk@GeekDevOps ~]$ awk '/a/ {print $3 " " $4}' marks.txt
Maths 90
Biology 87
English 85
History 89複製程式碼
匹配指定檔案中帶u的行,並列印第四列與第三列,中間以一個橫向製表符隔開。
[awk@GeekDevOps ~]$ awk '/u/ {print $4"\t"$3}' marks.txt
90 Maths複製程式碼
4.6 統計文字總行數
[awk@GeekDevOps ~]$ awk 'BEGIN{ct=0} {++ct} END{print "Count:",ct}' marks.txt
Count: 5複製程式碼
此行命令中,BEGIN部分其實是可以省略的。
4.7 列印匹配模式的總行數
[awk@GeekDevOps ~]$ awk '/a/ {++ct} END{print "Count:",ct}' marks.txt
Count: 4複製程式碼
此處的BEGIN部分不能出現,否則報錯。
4.8 列印超過指定長度的行
[awk@GeekDevOps ~]$ awk 'length($0) > 22' marks.txt
1) Amit Physics 80
2) Rahul Maths 90
3) Shyam Biology 87
4) Kedar English 85
5) Hari History 89複製程式碼
mark.txt檔案是通過空格來控制對齊的,每一行加上空格一共是22個字元。
4.9 ARGC命令列引數個數
[awk@GeekDevOps ~]$ awk 'BEGIN{print "Arguments=" ARGC}' One Two Three Four
Arguments=5複製程式碼
4.10 ARGV命令列引數陣列
[awk@GeekDevOps ~]$ awk 'BEGIN{for (i=0 ;i<ARGC-1;++i ){printf "ARGC[%d]=%s\n",i,ARGV[i]}}' One Two Three Four
ARGC[0]=awk
ARGC[1]=One
ARGC[2]=Two
ARGC[3]=Three複製程式碼
4.11 ENVIRON環境變數
[awk@GeekDevOps ~]$ awk 'BEGIN{print ENVIRON["PATH"]}'
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/awk/.local/bin:/home/awk/bin複製程式碼
4.12 FILENAME當前檔名
[awk@GeekDevOps ~]$ awk 'END{print FILENAME}' marks.txt
marks.txt複製程式碼
注意:此處使用的是END,如果省略則會列印出5個檔名稱,這個檔案一5行。
4.13 常用的awk內建變數
變數名 | 屬性 |
---|---|
$0 | 當前記錄 |
1 − | 當前記錄的第N個欄位 |
FS | 讀入欄位的分隔符,預設為空格 |
RS | 讀入記錄分隔符,預設為換行符 |
NF | 列,當前記錄中的欄位個數 |
NR | 行,已經讀出的行數,也就是行號 |
OFS | 輸出欄位分隔符,預設為空格 |
ORS | 輸出記錄分隔符,預設為換行符 |
[awk@GeekDevOps ~]$ awk -F " " '{print $4}' marks.txt
80
90
87
85
89
[awk@GeekDevOps ~]$ awk '{print $3}' marks.txt
Physics
Maths
Biology
English
History
[awk@GeekDevOps ~]$ awk 'BEGIN{FS=" "}{print $3}' marks.txt
Physics
Maths
Biology
English
History複製程式碼
以上三種寫法都是一樣的效果。
[awk@GeekDevOps ~]$ ls -al | awk 'BEGIN{size=0}{size+=$5}END{print size/1024/1024 "MB"}'
128.017MB
[awk@GeekDevOps ~]$ ls -al|awk 'NR>1{size+=$5} END{print size/1024/1024 "MB"}'
128.017MB複製程式碼
[awk@GeekDevOps ~]$ ls -al|awk 'NR==1{print $2/1024 "MB"}'
128.035MB複製程式碼
以上兩個程式碼片中,都是統計當前目錄下所有的檔案(包括隱藏檔案)所佔磁碟空間的大小,非常有用。
[awk@GeekDevOps ~]$ awk 'BEGIN{print "OFS=" OFS}' marks.txt
OFS=複製程式碼
4.14 awk中的算術運算
在awk中,支援像C語言中一樣的算術運算。在前面的文章中介紹過,Linux系統中無法對非整型數字直接進行算術運算,要對浮點型的資料就行算術運算我們可以使用awk來實現。
[awk@GeekDevOps ~]$ awk 'BEGIN{A=2.8;B=7;print A/B}'
0.4複製程式碼
在這一部分中,awk中的增減運算子、賦值運算子、關係操作符、邏輯運算子、三元操作符等均與C語言類似,不贅述。
4.15 awk中的一元操作符
[awk@GeekDevOps ~]$ awk 'BEGIN{A=2.8;A=+A;print A}'
2.8複製程式碼
4.16 awk中的指數操作符
[awk@GeekDevOps ~]$ awk 'BEGIN{A=2.8;A=A^3;print A}'
21.952
[awk@GeekDevOps ~]$ awk 'BEGIN{A=2.8;A=A**3;print A}'
21.952複製程式碼
4.17 awk中的字串連線操作符
[awk@GeekDevOps ~]$ awk 'BEGIN{str1="Hello";str2=",GeekDevOps";str3=str1 str2;print str3}'
Hello,GeekDevOps複製程式碼
4.18 awk中的陣列
陣列的定義與C語言有類似的地方,也有差別,使用的時候需要注意一下,有的資料上說awk中不支援多維陣列,通過awk的GUN的文件,我們可以看到,awk也是支援多維陣列的。刪除陣列元素使用delete語句:
[awk@GeekDevOps ~]$ awk 'BEGIN{array[0][0]=2;array[0][1]=3;array[1][0]=4;array[1][1]=8;for(i=0;i<2;i++){for(j=0;j<2;j++){printf "array[%d][%d]=%d\n",i,j, array[i][j]}}delete array[0][0];print array[0][0] "\t" array[0][1]}'
array[0][0]=2
array[0][1]=3
array[1][0]=4
array[1][1]=8
3複製程式碼
4.19 awk中的控制結構
在awk中,也支援類似C語言一樣的程式結構,支援if-else、while、do-while、for、switch、break、continue、nex、nextfile、exit。相信很多小夥伴都學習過C語言,與C語言一樣的就不在贅述。
[awk@GeekDevOps ~]$ awk 'NF != 4 {
printf("%s:%d: skipped: NF != 4\n", FILENAME, FNR) > "/dev/stderr"
next
}'
-:1: skipped: NF != 4
-:2: skipped: NF != 4
...複製程式碼
[awk@GeekDevOps ~]$ awk 'BEGIN {
if (("date" | getline date_now) <= 0) {
print "Can not get system date" > "/dev/stderr"
exit 1
}
print "Current date is", date_now
close("date")
}'
Current date is 2018年 02月 28日 星期三 23:05:16 CST複製程式碼
4.20 awk中的函式
在awk中,支援內建函式與使用者自定義函式。
常用數學計算函式
atan2(y, x)
Return the arctangent of y / x in radians. You can use ‘pi = atan2(0, -1)’ to retrieve the value of pi.複製程式碼
cos(x)
Return the cosine of x, with x in radians.複製程式碼
exp(x)
Return the exponential of x (e ^ x) or report an error if x is out of range. The range of values x can have depends on your machine’s floating-point representation.複製程式碼
int(x)
Return the nearest integer to x, located between x and zero and truncated toward zero. For example, int(3) is 3, int(3.9) is 3, int(-3.9) is -3, and int(-3) is -3 as well.複製程式碼
log(x)
Return the natural logarithm of x, if x is positive; otherwise, return NaN (“not a number”) on IEEE 754 systems. Additionally, gawk prints a warning message when x is negative.複製程式碼
rand()
Return a random number. The values of rand() are uniformly distributed between zero and one. The value could be zero but is never one.複製程式碼
sin(x)
Return the sine of x, with x in radians.複製程式碼
sqrt(x)
Return the positive square root of x. gawk prints a warning message if x is negative. Thus, sqrt(4) is 2.複製程式碼
srand([x])
Set the starting point, or seed, for generating random numbers to the value x.
複製程式碼
字串函式
asort(arr [, d [, how] ])
asorti(arr [, d [, how] ])
gsub(regex, sub, string)
index(str, sub)
length(str)
match(str, regex)
split(str, arr, regex)
sprintf(format, expr-list)
strtonum(str)
sub(regex, sub, string)
substr(str, start, l)
tolower(str)
toupper(str)
複製程式碼
時間函式
systime
mktime(datespec)
strftime([format [, timestamp[, utc-flag]]])
複製程式碼
位元組操作函式
and
compl
lshift
rshift
or
xor複製程式碼
自定義函式類似於C語言,參閱GUN網站function部分。
4.21 awk中的正規表示式
[awk@GeekDevOps ~]$ echo -e "My name is IVAN DU.\nMy blog is GeekDevOps.\nWelcome to my blog."|awk '/My+/'
My name is IVAN DU.
My blog is GeekDevOps.
[awk@GeekDevOps ~]$ echo -e "My name is IVAN DU.\nMy blog is GeekDevOps.\nWelcome to my blog."|awk '/^W/'
Welcome to my blog.複製程式碼
五、參考資料
用雲棲社群APP,舒服~