Unix文本處理工具之a(chǎn)wk

Unix命令行下輸入的命令是文本,輸出也都是文本。因此,掌握Unix文本處理工具是很重要的一種能力。awk是Unix常用的文本處理工具中的一種,它是以其發(fā)明者(Aho,Weinberger和Kernighan)的名字首字符命名的,是一種基于模式匹配檢查輸入然后將期望的匹配結(jié)果處理后輸出到屏幕的文本數(shù)據(jù)處理工具。

1、awk命令格式

awk ‘模式 {操作}’ 文件1 文件2 ……

awk命令的工作過程是這樣的:對于每一個輸入文件,逐行對其進行檢查,如果該行和awk命令參數(shù)的‘模式’部分匹配,則對該行執(zhí)行命令參數(shù)‘{操作}’部分所代表的操作。下面是一個簡單的例子:

$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命令的內(nèi)建變量,代表文件的行號。這樣,便可以對所有行號為1的行進行打印輸出。

2、語法說明

2.1 常用的內(nèi)建變量

變量 含義
NR 當前處理行的行號
FS 字段分隔,默認為空格或TAB
$n 當前處理行的第n個字段
$0 當前處理行的全部內(nèi)容
$NF 表示當前處理行的最后一個字段

這里要解釋下字段的含義:在awk的使用中,字段分隔符將文件的一行分隔為各個部分,每一個部分稱為一個字段,從左到右分別為第1個字段,……,第n個字段,其中,第0個字段是指這一整行。字段分隔符如果沒有特殊指定,則默認為空格或tab制表符。

2.2 在字段中匹配

awk可以支持根據(jù)特定字段內(nèi)容的匹配,操作符是~。該操作符的否定是!~,表示不匹配。下面是幾個例子:

  • 輸出第三個字段包含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
    
  • 同理,可實現(xiàn)在行與行之間添加空行

      $ 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的作用是程序結(jié)束后執(zhí)行掃尾的工作。

任何在BEGIN之后列出的操作(在{}內(nèi))將在Unix awk開始掃描輸入之前執(zhí)行,而END之后列出的操作將在掃描完全部的輸入之后執(zhí)行。因此,通常使用BEGIN來顯示變量和預(yù)置(初始化)變量,使用END來輸出最終結(jié)果。

下面是一個例子:

$ 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
    
  • 只打印奇數(shù)行(偶數(shù)行)

      $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結(jié)尾的行

      $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對文件中的行按重復(fù)次數(shù)排序

下面的文件是從數(shù)據(jù)庫中導(dǎo)出的一些數(shù)據(jù)(一部分),但是后續(xù)發(fā)現(xiàn)有些字段不需要。而如果重新從數(shù)據(jù)庫中導(dǎo)出生成的話,耗費的時間太長,這里就用到了awk命令了。

文件test.txt的內(nèi)容如下,每行有三個字段,字段之間用“ ::: ”隔開:

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

現(xiàn)在想只取出第后面的兩個字段去掉前面的字段:

$ 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ā)現(xiàn)前面有多余的不想要的空格,一點都不優(yōu)雅。原來-F指定的分隔符是要將空格轉(zhuǎn)義才能生效。

$ 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

這樣就好多了,但是,現(xiàn)在又想將上面的兩個字段還是用原來的“ ::: ”隔開。

$ 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ā)現(xiàn)有重復(fù)的話,可以進行進一步的去重。這里隨意生成了一個有重復(fù)的test.txt來進行操作,其內(nèi)容如下:

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生成的結(jié)果排序:

$ 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

如果需要根據(jù)重復(fù)次數(shù)排序,可以用“awk -F\ :::\ '{print $2,":::",$3}' test.txt | sort | uniq -c | sort -rn”這里sort的-n選項是指定根據(jù)每行第一個字段的數(shù)字值的大小排序,比如30比4大,如果沒有-n那么就是默認字典序排,4比30大。而unique中的-c選項是指定在每行之前加一個重復(fù)次數(shù)字段。
這里為了排序用sort -n先將數(shù)字字段放到前面,然后排序,排完之后,再將數(shù)字字段放到后面(實際上,可以用更加優(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命令將文本文件中的數(shù)字相加

這里有一個文本文件,其中,每行的第二個字段是一個數(shù)字,現(xiàn)在想要將每行的數(shù)字加起來,應(yīng)該如何操作呢?下面給出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命令的語法十分復(fù)雜,上面說到的只是很少的一部分。從這個例子中的用法來看,我們也能夠知道前面提到的awk命令的基本格式awk ‘模式 {操作}’ 文件1 文件2 ……中的,'模式 {操作}'單位實際上,是可以有多個的,也就是說可以是awk ‘模式 {操作}模式 {操作}... ...’ 文件1 文件2 ……。awk會逐個檢查每個模式,然后對符合模式的行執(zhí)行相應(yīng)的操作。下面是一個例子:

$ awk '{sum+=$2}NR <5 1{print $1}END{print sum}' awk_sum_test.txt    
apple
google
sammung
moto
xiaomi
lenevo
55
$ 

實際上,這個例子中的問題也可以用linux shell下讀取文件的方法來解決,只不過稍微有點麻煩。關(guān)于awk命令,個人認為只能是邊用邊學(xué),以后遇到比較好的例子,還會貼在在這里。_

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)載 原文的排版和內(nèi)容都更加友好,并且詳細,我只是在這里貼出了一部分留作自己以后參考和學(xué)習(xí),如希望更詳細了解AWK...
    XKirk閱讀 3,365評論 2 25
  • 本章主要學(xué)習(xí)內(nèi)容awk介紹 ?awk基本用法 ?awk變量 ?awk格式化 ?awk操作符 ?awk條件判斷 ?a...
    楠人幫閱讀 1,369評論 0 8
  • awk介紹awk變量printf命令:實現(xiàn)格式化輸出操作符awk patternawk actionawk數(shù)組aw...
    哈嘍別樣閱讀 1,737評論 0 4
  • netstat -tnlp|egrep -i "$1"|awk {'print $7'}|awk -F'/' '{...
    JerichoYu閱讀 1,082評論 0 0
  • 1、Nginx日志分析日志格式:'$remote_addr - $remote_user [$time_local...
    運維前線閱讀 858評論 0 4

友情鏈接更多精彩內(nèi)容