Unix命令行下輸入的命令是文本翁垂,輸出也都是文本。因此,掌握Unix文本處理工具是很重要的一種能力。awk是Unix常用的文本處理工具中的一種方妖,它是以其發(fā)明者(Aho,Weinberger和Kernighan)的名字首字符命名的,是一種基于模式匹配檢查輸入然后將期望的匹配結果處理后輸出到屏幕的文本數據處理工具罚攀。
1党觅、awk命令格式
awk ‘模式 {操作}’ 文件1 文件2 ……
awk命令的工作過程是這樣的:對于每一個輸入文件,逐行對其進行檢查斋泄,如果該行和awk命令參數的‘模式’部分匹配杯瞻,則對該行執(zhí)行命令參數‘{操作}’部分所代表的操作。下面是一個簡單的例子:
$cat awk_test.txt
1 a a,b,d,f
2 b alsdjf,apple,kdjf
3 c 163.2.201.1
4 d www.google.com
5 e http://blog.csdn.net/xia7139
$awk 'NR==1{print}' awk_test.txt
1 a a,b,d,f
上面的例子中炫掐,用awk命令輸出了awk_test.txt文件的第一行魁莉,其中命令的模式部分所用的NR是awk命令的內建變量,代表文件的行號募胃。這樣旗唁,便可以對所有行號為1的行進行打印輸出。
2痹束、語法說明
2.1 常用的內建變量
變量 | 含義 |
---|---|
NR | 當前處理行的行號 |
FS | 字段分隔检疫,默認為空格或TAB |
$n | 當前處理行的第n個字段 |
$0 | 當前處理行的全部內容 |
$NF | 表示當前處理行的最后一個字段 |
這里要解釋下字段的含義:在awk的使用中,字段分隔符將文件的一行分隔為各個部分参袱,每一個部分稱為一個字段电谣,從左到右分別為第1個字段秽梅,……,第n個字段剿牺,其中企垦,第0個字段是指這一整行。字段分隔符如果沒有特殊指定晒来,則默認為空格或tab制表符钞诡。
2.2 在字段中匹配
awk可以支持根據特定字段內容的匹配,操作符是~
湃崩。該操作符的否定是!~
荧降,表示不匹配。下面是幾個例子:
-
輸出第三個字段包含a的行攒读。
$ awk '$3 ~ /a/ {print}' awk_test.txt 1 a a,b,d,f 2 b alsdjf,apple,kdjf 5 e http://blog.csdn.net/xia7139
-
輸出第三個字段不包含a的行
$ awk '$3 !~ /a/ {print}' awk_test.txt 3 c 163.2.201.1 4 d www.google.com
2.3 awk變量
awk命令是支持變量的朵诫,定義變量的選項是-v
,下面是一個例子薄扁。
-
打印第二個字段包含b的行
$ var=b $ awk -v x=$var '$2 ~ x{print}' awk_test.txt 2 b alsdjf,apple,kdjf
-
打印第二個字段不包含b的行
$ var=b $ awk -v x=$var '$2 !~ x{print}' awk_test.txt 1 a a,b,d,f 3 c 163.2.201.1 4 d www.google.com 5 e http://blog.csdn.net/xia7139
但是剪返,變量在//
中不起作用:
$ var=b
$ awk -v x=$var '$3 ~ /x/{print}' awk_test.txt
5 e http://blog.csdn.net/xia7139
可以認為awk -v x=$var '$2 ~ x{print}' awk_test.txt
就相當于awk '$2 ~ /b/{print}' awk_test.txt
,也就是說會將變量的值當作正則表達式匹配。下面的例子可以說明這個問題:
$ var=.
$ awk -v x=$var '$3 ~ x{print}' awk_test.txt
1 a a,b,d,f
2 b alsdjf,apple,kdjf
3 c 163.2.201.1
4 d www.google.com
5 e http://blog.csdn.net/xia7139
$ var=\\\\.
$ awk -v x=$var '$3 ~ x{print}' awk_test.txt
3 c 163.2.201.1
4 d www.google.com
5 e http://blog.csdn.net/xia7139
2.4 awk默認的行分隔符和列分隔符
awk中有兩個特殊的變量ORS和OFS分別記錄著其缺省的行分隔符邓梅,ORS的默認值為換行\n
脱盲,OFS的默認值為空格。因此日缨,默認的awk會用它們來分隔行和列钱反。下面是幾個例子:
-
當我們試圖用
=
分隔各個字段時$ awk '{print $1,"=",$2,"=",$3}' awk_test.txt 1 = a = a,b,d,f 2 = b = alsdjf,apple,kdjf 3 = c = 163.2.201.1 4 = d = www.google.com 7 = d = www.google.com 4 = d = www.googlecom 5 = e = http://blog.csdn.net/xia7139
可以看到,這樣
=
被作為awk的一個字段輸出匣距,和其它字段之間用空格隔開面哥,實際上相當于"?=?"作為了分隔符分隔了各個原來的字段。 -
只用
=
分隔各個字段這時候就需要將OFS置為空字符串毅待,如下:
$ awk 'BEGIN{OFS=""}{print $1,"=",$2,"=",$3}' awk_test.txt 1=a=a,b,d,f 2=b=alsdjf,apple,kdjf 3=c=163.2.201.1 4=d=www.google.com 7=d=www.google.com 4=d=www.googlecom 5=e=http://blog.csdn.net/xia7139
-
同理幢竹,可實現在行與行之間添加空行
$ awk 'BEGIN{ORS="\n\n"}{print $1,"=",$2,"=",$3}' awk_test.txt | head -n 5 1 = a = a,b,d,f 2 = b = alsdjf,apple,kdjf 3 = c = 163.2.201.1
2.5 awk中的BEGIN和END
BEGIN作用是執(zhí)行一些初始化操作,END的作用是程序結束后執(zhí)行掃尾的工作恩静。
任何在BEGIN之后列出的操作(在{}內)將在Unix awk開始掃描輸入之前執(zhí)行,而END之后列出的操作將在掃描完全部的輸入之后執(zhí)行蹲坷。因此驶乾,通常使用BEGIN來顯示變量和預置(初始化)變量,使用END來輸出最終結果循签。
下面是一個例子:
$ awk 'BEGIN{product=1}{print $1,"=",$2,"=",$3;product=product*$1}END{printf "product: %.3f\n",product}' awk_test.txt
1 = a = a,b,d,f
2 = b = alsdjf,apple,kdjf
3 = c = 163.2.201.1
4 = d = www.google.com
7 = d = www.google.com
4 = d = www.googlecom
5 = e = http://blog.csdn.net/xia7139
product: 3360.000
$
3级乐、幾個例子及其輸出
3.1 下面的例子都是對上文中的awk_test.txt文件的操作
3.1.1 按行號操作
-
打印文件的1-3行
$awk 'NR==1,NR==3{print}' awk_test.txt 1 a a,b,d,f 2 b alsdjf,apple,kdjf 3 c 163.2.201.1
-
打印文件的第1行和第3行
$awk 'NR==1||NR==3{print}' awk_test.txt 或者是 $awk '(NR==1)||(NR==3){print}' awk_test.txt 1 a a,b,d,f 3 c 163.2.201.1
-
只打印奇數行(偶數行)
$awk '(NR%2)==1{print}' awk_test.txt 1 a a,b,d,f 3 c 163.2.201.1 5 e http://blog.csdn.net/xia7139 $awk '(NR%2)==0{print}' awk_test.txt 2 b alsdjf,apple,kdjf 4 d www.google.com
3.1.2 使用正則表達式
-
打印包含2的行
$awk '/2/{print}' awk_test.txt 2 b alsdjf,apple,kdjf 3 c 163.2.201.1
-
打印以com結尾的行
$awk '/com$/{print}' awk_test.txt 4 d www.google.com
3.1.3 指定分隔,輸出指定字段
-
打印第1-3行的第一個字段和第三個字段
$awk 'NR==1,NR==3{print $1,$3}' awk_test.txt 1 a,b,d,f 2 alsdjf,apple,kdjf 3 163.2.201.1
-
指定分隔符為.县匠,輸出第二個字段為csdn的行的第三個字段和整行
$awk -F. '$2=="csdn"{print $3,$0}' awk_test.txt net/xia7139 5 e http://blog.csdn.net/xia7139
-
指定分隔符為.风科,輸出每行的最后一個字段
$ awk -F. '{print $NF}' awk_test.txt 1 a a,b,d,f 2 b alsdjf,apple,kdjf 1 com net/xia7139
3.2 awk對文件中的行按重復次數排序
下面的文件是從數據庫中導出的一些數據(一部分)撒轮,但是后續(xù)發(fā)現有些字段不需要。而如果重新從數據庫中導出生成的話贼穆,耗費的時間太長题山,這里就用到了awk命令了。
文件test.txt的內容如下故痊,每行有三個字段顶瞳,字段之間用“ ::: ”隔開:
11 ::: Thomas R. Dean ::: 54
14 ::: Johann van Rensburg ::: 1
75 ::: Arun G. Phadke ::: 13
81 ::: Tiffany M. Frazier ::: 2
84 ::: Sridhar R. Iyer ::: 1
95 ::: Leesa Murray ::: 11
96 ::: David S. Munro ::: 34
104 ::: David R. Lovell ::: 2
112 ::: Steffen Rusitschka ::: 3
161 ::: Peter Forbrig ::: 116
現在想只取出第后面的兩個字段去掉前面的字段:
$ awk -F ::: '{print $2,$3}' test.txt
Thomas R. Dean 54
Johann van Rensburg 1
Arun G. Phadke 13
Tiffany M. Frazier 2
Sridhar R. Iyer 1
Leesa Murray 11
David S. Munro 34
David R. Lovell 2
Steffen Rusitschka 3
Peter Forbrig 116
發(fā)現前面有多余的不想要的空格,一點都不優(yōu)雅愕秫。原來-F指定的分隔符是要將空格轉義才能生效慨菱。
$ awk -F\ :::\ '{print $2,$3}' test.txt
Thomas R. Dean 54
Johann van Rensburg 1
Arun G. Phadke 13
Tiffany M. Frazier 2
Sridhar R. Iyer 1
Leesa Murray 11
David S. Munro 34
David R. Lovell 2
Steffen Rusitschka 3
Peter Forbrig 116
這樣就好多了,但是戴甩,現在又想將上面的兩個字段還是用原來的“ ::: ”隔開符喝。
$ awk -F\ :::\ '{print $2,":::",$3}' test.txt
Thomas R. Dean ::: 54
Johann van Rensburg ::: 1
Arun G. Phadke ::: 13
Tiffany M. Frazier ::: 2
Sridhar R. Iyer ::: 1
Leesa Murray ::: 11
David S. Munro ::: 34
David R. Lovell ::: 2
Steffen Rusitschka ::: 3
Peter Forbrig ::: 116
Wow, it is beautiful!
下面如果發(fā)現有重復的話,可以進行進一步的去重甜孤。這里隨意生成了一個有重復的test.txt來進行操作协饲,其內容如下:
11 ::: Thomas R. Dean ::: 54
1411 ::: Johann van Rensburg ::: 1
106 ::: Peter Forbrig ::: 116
141 ::: Johann van Rensburg ::: 1
143 ::: Johann van Rensburg ::: 1
75 ::: Arun G. Phadke ::: 13
844 ::: Sridhar R. Iyer ::: 1
149 ::: Johann van Rensburg ::: 1
81 ::: Tiffany M. Frazier ::: 2
84 ::: Sridhar R. Iyer ::: 1
95 ::: Leesa Murray ::: 11
96 ::: David S. Munro ::: 34
104 ::: David R. Lovell ::: 2
15 ::: Johann van Rensburg ::: 1
112 ::: Steffen Rusitschka ::: 3
12 ::: Steffen Rusitschka ::: 3
161 ::: Peter Forbrig ::: 116
106 ::: Peter Forbrig ::: 116
首先對awk生成的結果排序:
$ awk -F\ :::\ '{print $2,":::",$3}' test.txt | sort
Arun G. Phadke ::: 13
David R. Lovell ::: 2
David S. Munro ::: 34
Johann van Rensburg ::: 1
Johann van Rensburg ::: 1
Johann van Rensburg ::: 1
Johann van Rensburg ::: 1
Johann van Rensburg ::: 1
Leesa Murray ::: 11
Peter Forbrig ::: 116
Peter Forbrig ::: 116
Peter Forbrig ::: 116
Sridhar R. Iyer ::: 1
Sridhar R. Iyer ::: 1
Steffen Rusitschka ::: 3
Steffen Rusitschka ::: 3
Thomas R. Dean ::: 54
Tiffany M. Frazier ::: 2
然后,進行去重课蔬,之所以進行排序囱稽,是因為uniq命令只能對相鄰行進行去重。
$ awk -F\ :::\ '{print $2,":::",$3}' test.txt | sort | uniq
Arun G. Phadke ::: 13
David R. Lovell ::: 2
David S. Munro ::: 34
Johann van Rensburg ::: 1
Leesa Murray ::: 11
Peter Forbrig ::: 116
Sridhar R. Iyer ::: 1
Steffen Rusitschka ::: 3
Thomas R. Dean ::: 54
Tiffany M. Frazier ::: 2
如果需要根據重復次數排序二跋,可以用“awk -F\ :::\ '{print $2,":::",$3}' test.txt | sort | uniq -c | sort -rn”這里sort的-n選項是指定根據每行第一個字段的數字值的大小排序战惊,比如30比4大,如果沒有-n那么就是默認字典序排扎即,4比30大吞获。而unique中的-c選項是指定在每行之前加一個重復次數字段。
這里為了排序用sort -n先將數字字段放到前面谚鄙,然后排序各拷,排完之后,再將數字字段放到后面(實際上闷营,可以用更加優(yōu)雅的方法烤黍,直接指定按第二個字段排序就可以,這里用的是先顛過來傻盟,然后再倒回去的方法速蕊。
$ awk -F\ :::\ '{print $3,":::",$2}' test.txt | sort | uniq | sort -rn | awk -F\ :::\ '{print $2,":::",$1}'
Peter Forbrig ::: 116
Thomas R. Dean ::: 54
David S. Munro ::: 34
Arun G. Phadke ::: 13
Leesa Murray ::: 11
Steffen Rusitschka ::: 3
Tiffany M. Frazier ::: 2
David R. Lovell ::: 2
Sridhar R. Iyer ::: 1
Johann van Rensburg ::: 1
3.3 awk命令將文本文件中的數字相加
這里有一個文本文件,其中娘赴,每行的第二個字段是一個數字规哲,現在想要將每行的數字加起來,應該如何操作呢诽表?下面給出awk命令的版本:
$ cat awk_sum_test.txt
apple 1
google 2
sammung 3
moto 4
xiaomi 5
smartisan 6
oppo 7
huawei 8
coolpad 9
lenevo 10
$ awk '{sum+=$2}END{print sum}' awk_sum_test.txt
55
$
從這個命令可以看出唉锌,awk命令的使用還是比較方便的隅肥。初次之外,要知道awk命令的語法十分復雜袄简,上面說到的只是很少的一部分腥放。從這個例子中的用法來看,我們也能夠知道前面提到的awk命令的基本格式awk ‘模式 {操作}’ 文件1 文件2 ……中的痘番,'模式 {操作}'單位實際上捉片,是可以有多個的,也就是說可以是awk ‘模式 {操作}模式 {操作}... ...’ 文件1 文件2 ……汞舱。awk會逐個檢查每個模式伍纫,然后對符合模式的行執(zhí)行相應的操作。下面是一個例子:
$ awk '{sum+=$2}NR <5 1{print $1}END{print sum}' awk_sum_test.txt
apple
google
sammung
moto
xiaomi
lenevo
55
$
實際上昂芜,這個例子中的問題也可以用linux shell下讀取文件的方法來解決莹规,只不過稍微有點麻煩。關于awk命令泌神,個人認為只能是邊用邊學良漱,以后遇到比較好的例子,還會貼在在這里欢际。_