Linux三劍客之awk精講(基礎與進階)
標簽(空格分隔): Linux實戰教學筆記-陳思齊
快捷跳轉目錄:
##第1章 awk基礎入門
###1.1 awk簡介要弄懂awk程序,必須熟悉了解這個工具的規則。本實戰筆記的目的是通過實際案例或面試題帶同學們熟練掌握awk在企業中的用法,而不是awk程序的幫助手冊。
- 一種名字怪異的語言
- 模式掃描和處理
###1.2 學完awk你可以掌握:awk不僅僅時linux系統中的一個命令,而且是一種編程語言,可以用來處理數據和生成報告(excel)。處理的數據可以是一個或多個文件,可以是來自標准輸入,也可以通過管道獲取標准輸入,awk可以在命令行上直接編輯命令進行操作,也可以編寫成awk程序來進行更為復雜的運用。本章主要講解awk命令的運用。
- 記錄與字段
- 模式匹配:模式與動作
- 基本的awk執行過程
- awk常用內置變量(預定義變量)
- awk數組(工作常用)
- awk語法:循環,條件
- awk常用函數
- 向awk傳遞參數
- awk引用shell變量
- awk小程序及調試思路
[root@chensiqi1 ~]# cat /etc/redhat-release
CentOS release 6.8 (Final)
[root@chensiqi1 ~]# uname -r
2.6.32-642.el6.x86_64
[root@chensiqi1 ~]# ll `which awk`
lrwxrwxrwx. 1 root root 4 Dec 23 20:25 /bin/awk -> gawk
[root@chensiqi1 ~]# awk --version
GNU Awk 3.1.7
Copyright (C) 1989, 1991-2009 Free Software Foundation.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/.
###1.4 awk的格式
- awk指令是由模式,動作,或者模式和動作的組合組成。
- 模式既pattern,可以類似理解成sed的模式匹配,可以由表達式組成,也可以是兩個正斜杠之間的正則表達式。比如NR==1,這就是模式,可以把他理解為一個條件。
- 動作即action,是由在大括號里面的一條或多條語句組成,語句之間使用分號隔開。比如awk使用格式:

awk處理的內容可以來自標准輸入(<),一個或多個文本文件或管道。

- pattern既模式,也可以理解為條件,也叫找誰,你找誰?高矮,胖瘦,男女?都是條件,既模式。
- action既動作,可以理解為干啥,找到人之后你要做什么。
模式和動作的詳細介紹我們放在后面部分,現在大家先對awk結構有一個了解。
示例1-1: 基本的模式和動作
[root@chensiqi1 ~]# awk -F ":" 'NR>=2 && NR<=6{print NR,$1}' /etc/passwd
2 bin
3 daemon
4 adm
5 lp
6 sync
命令說明:
-F 指定分隔符為冒號,相當於以“:”為菜刀,進行字段的切割。
NR>=2 && NR<=6:這部分表示模式,是一個條件,表示取第2行到第6行。
{print NR,$1}:這部分表示動作,表示要輸出NR行號和$1第一列。
示例1-2 只有模式
[root@chensiqi1 ~]# awk -F ":" 'NR>=2&&NR<=6' /etc/passwd
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
命令說明:
-F指定分隔符為冒號
NR>=2&&NR<=6這部分是條件,表示取第2行到第6行。
但是這里沒有動作,這里大家需要了解如果只有條件(模式)沒有動作,awk默認輸出整行
示例1-3:只有動作
[root@chensiqi1 ~]# awk -F ":" '{print NR,$1}' /etc/passwd
1 root
2 bin
3 daemon
4 adm
5 lp
6 sync
7 shutdown
8 halt
9 mail
10 uucp
以下省略....
命令說明:
-F指定分隔符為冒號
這里沒有條件,表示對每一行都處理
{print NR,$1}表示動作,顯示NR行號與$1第一列
這里要理解沒有條件的時候,awk會處理每一行。
示例1-4:多個模式和動作
[root@chensiqi1 ~]# awk -F ":" 'NR==1{print NR,$1}NR==2{print NR,$NF}' /etc/passwd
1 root
2 /sbin/nologin
命令說明:
-F指定分隔符為冒號
這里有多個條件與動作的組合
NR==1表示條件,行號(NR)等於1的條件滿足的時候,執行{print NR,$1}動作,輸出行號與第一列。
NR==2表示條件,行號(NR)等於2的條件滿足的時候,執行{print NR,$NF}動作,輸出行號與最后一列($NF)
注意:
- Pattern和{Action}需要用單引號引起來,防止shell作解釋。
- Pattern是可選的。如果不指定,awk將處理輸入文件中的所有記錄。如果指定一個模式,awk則只處理匹配指定的模式的記錄。
- {Action}為awk命令,可以是但個命令,也可以多個命令。整個Action(包括里面的所有命令)都必須放在{ 和 }之間。
- Action必須被{ }包裹,沒有被{ }包裹的就是Patern
- file要處理的目標文件
在深入了解awk前,我們需要知道awk如何處理文件的。
示例1-5 示例文件的創建
[root@chensiqi1 ~]# mkdir /server/files/ -p
[root@chensiqi1 ~]# head /etc/passwd > /server/files/awkfile.txt
[root@chensiqi1 ~]# cat /server/files/awkfile.txt
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin
這個文件僅包含十行文件,我們使用下面的命令:
示例1-6 awk執行過程演示
[root@chensiqi1 ~]# awk 'NR>=2{print $0}' /server/files/awkfile.txt
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin
命令說明:
條件NR>=2,表示行號大於等於2時候,執行{print $0}顯示整行。
awk是通過一行一行的處理文件,這條命令中包含模式部分(條件)和動作部分(動作),awk將處理模式(條件)指定的行
####1.6.1 小結awk執行過程
1)awk讀入第一行內容
2)判斷是否符合模式中的條件NR>=2
a,如果匹配則執行對應的動作{print $0}
b,如果不匹配條件,繼續讀取下一行
3)繼續讀取下一行
4)重復過程1-3,直到讀取到最后一行(EOF:end of file)

| 名稱 | 含義 |
|---|---|
| record | 記錄,行 |
| field | 域,區域,字段,列 |
查看一下下面這段文字
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin
思考:
一共有多少行呢?你如何知道的?通過什么標志?
####1.7.2 記錄分隔符-RSawk對每個要處理的輸入數據認為都是具有格式和結構的,而不僅僅是一堆字符串。默認情況下,每一行內容都是一條記錄,並以換行符分隔(\n)結束

- awk默認情況下每一行都是一個記錄(record)
- RS既record separator輸入輸出數據記錄分隔符,每一行是怎么沒的,表示每個記錄輸入的時候的分隔符,既行與行之間如何分隔。
- NR既number of record 記錄(行)號,表示當前正在處理的記錄(行)的號碼。
- ORS既output record separator 輸出記錄分隔符。
awk使用內置變量RS來存放輸入記錄分隔符,RS表示的是輸入的記錄分隔符,這個值可以通過BEGIN模塊重新定義修改。
示例1-1:使用“/”為默認記錄分隔符
示例文件:
[root@chensiqi1 ~]# cat /server/files/awkfile.txt
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin
[root@chensiqi1 ~]# awk 'BEGIN{RS="/"}{print NR,$0}' /server/files/awkfile.txt
1 root:x:0:0:root:
2 root:
3 bin
4 bash
bin:x:1:1:bin:
5 bin:
6 sbin
7 nologin
daemon:x:2:2:daemon:
8 sbin:
9 sbin
10 nologin
adm:x:3:4:adm:
11 var
12 adm:
13 sbin
14 nologin
lp:x:4:7:lp:
15 var
16 spool
17 lpd:
18 sbin
19 nologin
sync:x:5:0:sync:
20 sbin:
21 bin
22 sync
shutdown:x:6:0:shutdown:
23 sbin:
24 sbin
25 shutdown
halt:x:7:0:halt:
26 sbin:
27 sbin
28 halt
mail:x:8:12:mail:
29 var
30 spool
31 mail:
32 sbin
33 nologin
uucp:x:10:14:uucp:
34 var
35 spool
36 uucp:
37 sbin
38 nologin
命令說明:
在每行的開始先打印輸出NR(記錄號行號),並打印出每一行$0(整行)的內容。
我們設置RS(記錄分隔符)的值為“/”,表示一行(記錄)以“/”結束
在awk眼中,文件是從頭到尾一段連續的字符串,恰巧中間有些\n(回車換行符),\n也是字符哦。
我們回顧下“行(記錄)”到底是什么意思?
- 行(記錄):默認以\n(回車換行)結束。而這個行的結束不就是記錄分隔符嘛。
- 所以在awk中,RS(記錄分隔符)變量表示着行的結束符號(默認是回車換行)
在工作中,我們可以通過修改RS變量的值來決定行的結束標志,最終來決定“每行”的內容。
為了方便人們理解,awk默認就把RS的值設置為“\n”
####1.7.3 對$0的認識注意:
awk的BEGIN模塊,我會在后面(模式-BEGIN模塊)詳細講解,此處大家僅需要知道在BEGIN模塊里面我們來定義一些awk內置變量即可。
- 如1.7.2的例子,可以看出awk中$0表示整行,其實awk使用$0來表示整條記錄。記錄分隔符存在RS變量中,或者說每個記錄以RS內置變量結束。
- 另外,awk對每一行的記錄號都有一個內置變量NR來保存,每處理完一條記錄,NR的值就會自動+1
- 下面通過示例來加深印象。
示例1-2:NR記錄號
[root@chensiqi1 ~]# awk '{print NR,$0}' /server/files/awkfile.txt
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6 sync:x:5:0:sync:/sbin:/bin/sync
7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
8 halt:x:7:0:halt:/sbin:/sbin/halt
9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
10 uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin
命令說明:
NR既number of record,當前記錄的記錄號,剛開始學也可以理解為行號。
$0表示整行或者說整個記錄
####1.7.4 企業面試題:按單詞出現頻率降序排序(計算文件中每個單詞的重復數量)
注:(此處使用sort與uniq即可)
題目:
題目創建方法:
sed -r '1,10s#[^a-zA-Z]+# #g' /etc/passwd>/server/files/count.txt
[root@chensiqi1 files]# cat /server/files/count.txt
root x root root bin bash
bin x bin bin sbin nologin
daemon x daemon sbin sbin nologin
adm x adm var adm sbin nologin
lp x lp var spool lpd sbin nologin
sync x sync sbin bin sync
shutdown x shutdown sbin sbin shutdown
halt x halt sbin sbin halt
mail x mail var spool mail sbin nologin
uucp x uucp var spool uucp sbin nologin
思路:
讓所有單詞排成一列,這樣每個單詞都是單獨的一行
1)設置RS值為空格
2)將文件里面的所有空格替換為回車換行符“\n”
3)grep所有連續的字母,grep -o參數讓他們排成一列。
方法一:
[root@chensiqi1 files]# awk 'BEGIN{RS="[ ]+"}{print $0}' count.txt | sort |uniq -c|sort
1
1 bash
1 lpd
2 daemon
2 lp
3 adm
3 halt
3 mail
3 root
3 shutdown
3 spool
3 sync
3 uucp
4 var
5 bin
6 nologin
10 x
12 sbin
方法二:
[root@chensiqi1 files]# cat count.txt | tr " " "\n" | sort | uniq -c | sort
1 bash
1 lpd
2 daemon
2 lp
3 adm
3 halt
3 mail
3 root
3 shutdown
3 spool
3 sync
3 uucp
4 var
5 bin
6 nologin
10 x
12 sbin
方法三:
[root@chensiqi1 files]# grep -o "[a-zA-Z]\+" count.txt | sort | uniq -c | sort
1 bash
1 lpd
2 daemon
2 lp
3 adm
3 halt
3 mail
3 root
3 shutdown
3 spool
3 sync
3 uucp
4 var
5 bin
6 nologin
10 x
12 sbin
####1.7.5 awk記錄知識小結
- NR存放着每個記錄的號(行號)讀取新行時候會自動+1
- RS是輸入數據的記錄的分隔符,簡單理解就是可以指定每個記錄的結尾標志。
- RS作用就是表示一個記錄的結束
- 當我們修改了RS的值,最好配合NR(行)來查看變化,也就是修改了RS的值通過NR查看結果,調試awk程序。
- ORS輸出數據的記錄的分隔符
####1.7.6 字段(列)awk學習技巧一則:
大象放冰箱分幾步?打開冰箱,把大象放進去,關閉冰箱門。
awk也是一樣的,一步一步來,先修改了RS,然后用NR調試,看看到底如何分隔的。然后通過sort排序,uniq -c去重
- 每條記錄都是由多個區域(field)組成的,默認情況下區域之間的分隔符是由空格(即空格或制表符)來分隔,並且將分隔符記錄在內置變量FS中,每行記錄的區域數保存在awk的內置變量NF中。

- FS既field separator,輸入字段(列)分隔符。分隔符就是菜刀,把一行字符串切為很多個區域。
- NF既number of fileds,表示一行中列(字段)的個數,可以理解為菜刀切過一行后,切成了多少份。
OFS輸出字段(列)分隔符
- awk使用內置變量FS來記錄區域分隔符的內容,FS可以在命令行上通過-F參數來更改,也可以通過BEGIN模塊來更改。
- 然后通過$n,n是整數,來取被切割后的區域,$1取第一個區域,$2取第二個區域,$NF取最后一個區域。
下面我們通過示例來加強學習。
示例1-3:指定分隔符
[root@chensiqi1 files]# awk -F ":" 'NR>=2&&NR<=5{print $1,$3}' /server/files/awkfile.txt
bin 1
daemon 2
adm 3
lp 4
命令說明:
以:(冒號)為分隔符,顯示第2行到第5行之間的第一區域和第三區域。
- 此處的FS知識一個字符,其實它可以指定多個的,此時FS指定的值可以是一個正則表達式。
- 正常情況下,當你指定分隔符(非空格)的時候,例如指定多個區域分隔符,每個分隔符就是一把刀,把左右兩邊切為兩個部分。
企業面試題:同時取出chensiqi和215379068這兩個內容(指定多分隔符)
[root@chensiqi1 files]# echo "I am chensiqi,my qq is 1234567890">>/server/files/chensiqi.txt
[root@chensiqi1 files]# cat /server/files/chensiqi.txt
I am chensiqi,my qq is 1234567890
同時取出chensiqi和1234567890這兩個內容。
思路:
我們用默認的想法一次使用一把刀,需要配合管道的。如何同時使用兩把刀呢?看下面的結果
[root@chensiqi1 files]# awk -F "[ ,]" '{print $3,$NF}' /server/files/chensiqi.txt
chensiqi 1234567890
命令說明:
通過命令-F參數指定區域分隔符
[ ,]是正則表達式里面的內容,它表示一個整體,“一個”字符,既空格或者逗號(,),合並在一起,-F “[ ,]”就表示以空格或者逗號(,)為區域分隔符
小技巧:
在動作(‘{print $3,$NF}’)里面的逗號,表示空格,其實動作中的逗號就是OFS的值,我們會在后面說明。剛開始大家把動作中的都逗號,當作空格即可。
示例:默認分隔符和指定分隔符會有些差異
[root@chensiqi1 files]# ifconfig eth0 | awk 'NR==2' >/server/files/awkblank.txt
[root@chensiqi1 files]# cat /server/files/awkblank.txt
inet addr:192.168.197.133 Bcast:192.168.197.255 Mask:255.255.255.0
#默認分隔符時候
[root@chensiqi1 files]# awk '{print $1}' /server/files/awkblank.txt
inet
#指定分隔符時候
[root@chensiqi1 files]# awk -F "[ :]+" '{print $1}' /server/files/awkblank.txt
[root@chensiqi1 files]# awk -F "[ :]+" '{print $2}' /server/files/awkblank.txt
inet
命令說明:
awk默認的FS分隔符對於空格序列,一個空格或多個空格tab都認為是一樣的,一個整體。
- 這個文件的開頭有很多連續的空格,然后才是inet這個字符
- 當我們使用默認的分隔符的時候,$1是有內容的。
- 當我們指定其他分隔符(非空格)時候,區域會有所變化
- 到底為何會這樣,我們在這里不再深入研究,只要了解有這種情況,注意一下即可。
現在說說ORS和OFS這兩個內置變量的含義。
- RS是輸入記錄分隔符,決定awk如何讀取或分隔每行(記錄)
- ORS表示輸出記錄分隔符,決定awk如何輸出一行(記錄)的,默認是回車換行(\n)
- FS是輸入區域分隔符,決定awk讀入一行后如何再分為多個區域。
- OFS表示輸出區域分隔符,決定awk輸出每個區域的時候使用什么分隔她們。
- awk無比強大,你可以通過RS,FS決定awk如何讀取數據。你也可以通過修改ORS,OFS的值指定awk如何輸出數據。

現在你應該會對awk的記錄字段有所了解了,下面我們總結一下,學會給階段性知識總結是學好運維的必備技能。

- RS記錄分隔符,表示每行的結束標志
- NR行號(記錄號)
- FS字段分隔符,每列的分隔標志或結束標志
- NF就是每行有多少列,每個記錄中字段的數量
$符號表示取某個列(字段),$1$2$NF- NF表示記錄中的區域(列)數量,
$NF取最后一個列(區域。) - FS(-F)字段(列)分隔符,-F(FS)“:”<==>‘BEGIN{FS=':'}’
- RS 記錄分隔符(行的結束標識)
- NR 行號
- 選好合適的刀FS(***),RS,OFS,ORS
- 分隔符==>結束標識
- 記錄與區域,你就對我們所謂的行與列,有了新的認識(RS,FS)
到了這里我們回頭看看,我們之前學習的內容。
- awk的命令行結構
- awk的模式和動作
- awk的記錄和字段
比較核心常用的是字段。
另外這些企業面試題可是學會awk的必備,必須自己也能寫出來。
接下來就詳細介紹下,awk的模式都有幾種:
- 正則表達式作為模式
- 比較表達式作為模式
- 范圍模式
- 特殊模式BEGIN和END
awk的模式是你玩好awk的必備也是最基礎的內容,必須熟練掌握
###2.2 正則表達式作為模式awk同sed一樣也可以通過模式匹配來對輸入的文本進行匹配處理。說到模式匹配,肯定少不了正則表達式,awk也支持大量的正則表達式模式,大部分與sed支持的元字符類似,而且正則表達式是玩轉三劍客的必備工具,下表列出了awk支持的正則表達式元字符:
awk默認就支持的元字符:
| 元字符 | 功能 | 示例 | 解釋 |
|---|---|---|---|
| ^ | 字符串開頭 | /chensiqi/或$3~/chensiqi/ | 匹配所有以chensiqi開頭的字符串;匹配出所有第三列中以chensiqi開頭的 |
| $ | 字符串結尾 | /chensiqi$/或$3~/chensiqi$/ | 匹配所有以chensiqi結尾的字符串;匹配第三列中以chensiqi結尾的 |
| .(點) | 匹配任意但個字符(包括回車符) | /c..l/ | 匹配字母c,然后兩個任意字符,再以l結尾的行 |
| * | 重復0個或多個前一個字符 | /a*cool/ | 匹配0個或多個a之后緊跟着cool的行 |
| + | 重復前一個字符一次或多次 | /a+b/ | 匹配一個或多個a加上字符串b的行 |
| ? | 匹配0個或一個前邊的字符 | /a?b/ | 匹配以字母a或b或c開頭的行 |
| [] | 匹配指定字符組內的任一個字符 | /[1]/ | 匹配以字母a或b或c開頭的行 |
| [^] | 匹配不在指定字符組內的任一字符 | /[abc]/ | 匹配不以字母a或b或c開頭的行 |
| () | 子表達式組合 | /(chensiqi)+/ | 表示一個或多個cool組合,當有一些字符需要組合時,使用括號括起來 |
| |
或者的意思 | /(chensiqi)|B/ |
匹配chensiqi或字母B的行 |
awk默認不支持的元字符:(參數--posix)
| 元字符 | 功能 | 示例 | 解釋 |
|---|---|---|---|
| x{m} | x字符重復m次 | /cool{5}/ | 匹配l字符5次 |
| x{m,} | x字符重復至少m次 | /(cool){2,}/ | 匹配cool整體,至少2次 |
| x{m,n} | x字符重復至少m次,但不超過n次 | /(cool){5,6}/ | 匹配cool整體,至少5次,最多6次 |
提示:
- 加括號代表整體匹配,不加那么就匹配前邊的一個字符。awk默認不支持這種形式的正則,需要加--posix參數或者--re-interval
- 正則表達式的運用,默認是在行內查找匹配的字符串,若有匹配則執行action操作,但是有時候僅需要固定的列來匹配指定的正則表達式,比如:我想取/etc/passwd文件中第五列{$5}這一列查找匹配mail字符串的行,這樣就需要用另外兩個匹配操作符,並且awk里面只有這兩個操作符來匹配正則表達式。
awk正則匹配操作符:
| ~ | 用於對記錄或區域的表達式進行匹配 |
|---|---|
| !~ | 用於表達與~相反的意思 |
下面還是通過具體示例來看看,awk如何來通過正則表達式匹配字符串的
####2.2.2 awk正則表達式匹配整行[root@chensiqi1 files]# awk -F ":" '/^root/' awkfile.txt
root:x:0:0:root:/root:/bin/bash
和下面的效果是一樣的
[root@chensiqi1 files]# awk -F ":" '$0~/^root/' awkfile.txt
root:x:0:0:root:/root:/bin/bash
提示:
awk只用正則表達式的時候是默認匹配整行的即‘$0~/root/’和‘/root/’是一樣的。
####2.2.3 awk正則表達式匹配一行中的某一列[root@chensiqi1 files]# awk -F ":" '$5~/shutdown/' awkfile.txt
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
提示:
- $5表示第五個區域(列)
- ~表示匹配(正則表達式匹配)
- /shutdown/表示匹配shutdown這個字符串
合並在一起
$5~/shutdown/表示第五個區域(列)匹配正則表達式/shutdown/,既第5列包含shutdown這個字符串,則顯示這一行。
####2.2.4 某個區域中的開頭和結尾知道了如何使用正則表達式匹配操作符之后,我們來看看awk正則與grep和sed不同的地方。
awk正則表達式
| ^ | 匹配一個字符串的開頭 |
|---|---|
| $ | 匹配一個字符串的結尾 |
在sed和grep這兩個命令中,我們都把它們當作行的開頭和結尾。但是在awk中他表示的是字符串的開頭和結尾。
接下來我們通過練習題來聯系awk如何使用正則表達式。
####2.2.5 創建測試環境[root@chensiqi1 ~]# cat >>/server/files/reg.txt<<KOF
Zhang Dandan 41117397 :250:100:175
Zhang Xiaoyu 390320151 :155:90:201
Meng Feixue 80042789 :250:60:50
Wu Waiwai 70271111 :250:80:75
Liu Bingbing 41117483 :250:100:175
Wang Xiaoai 3515064655 :50:95:135
Zi Gege 1986787350 :250:168:200
Li Youjiu 918391635 :175:75:300
Lao Nanhai 918391635 :250:100:175
KOF
####2.2.6 測試文件說明
Zhang Dandan 41117397 :250:100:175
Zhang Xiaoyu 390320151 :155:90:201
Meng Feixue 80042789 :250:60:50
Wu Waiwai 70271111 :250:80:75
Liu Bingbing 41117483 :250:100:175
Wang Xiaoai 3515064655 :50:95:135
Zi Gege 1986787350 :250:168:200
Li Youjiu 918391635 :175:75:300
Lao Nanhai 918391635 :250:100:175
說明:
- 第一列是姓氏
- 第二列是名字
- 第一列第二列合起來就是姓名
- 第三列是對應的ID號碼
- 最后三列是三次捐款數量
練習題1:顯示姓Zhang的人的第二次捐款金額及她的名字
練習題2:顯示Xiaoyu的名字和ID號碼
練習題3:顯示所有以41開頭的ID號碼的人的全名和ID號碼
練習題4:顯示所有以一個D或X開頭的人名全名
練習題5:顯示所有ID號碼最后一位數字是1或5的人的全名
練習題6:顯示Xiaoyu的捐款,每個值都有以$開頭。如$520$200$135
練習題7:顯示所有人的全名,以姓,名的格式顯示,如Meng,Feixue
####2.2.8 awk正則表達式練習題-詳解示例1:顯示姓Zhang的人的第二次捐款金額及她的名字
[root@chensiqi1 files]# cat reg.txt
Zhang Dandan 41117397 :250:100:175
Zhang Xiaoyu 390320151 :155:90:201
Meng Feixue 80042789 :250:60:50
Wu Waiwai 70271111 :250:80:75
Liu Bingbing 41117483 :250:100:175
Wang Xiaoai 3515064655 :50:95:135
Zi Gege 1986787350 :250:168:200
Li Youjiu 918391635 :175:75:300
Lao Nanhai 918391635 :250:100:175
[root@chensiqi1 files]# awk -F "[ :]+" '$1~/^Zhang/{print $2,$(NF-1)}' reg.txt
Zhang 100
Zhang 90
說明:
- -F指定分隔符,現在大家知道了-F即FS也是支持正則表達式的。
- 【 :】+ 表示連續的空格或冒號
- -F “【 :】+” 以連續的空格或冒號為分隔符
- /Zhang/表示條件,整行中包含Dan字符的這個條件。
- {print $1,$(NF-1)} 表示動作,滿足條件后,執行顯示第一列($1)和倒數第二列($(NF-1))當然$5也可以。
注意:
NF是一行中有多少列,NF-1整行就是倒數第二列。
$(NF-1)就是取倒數第二列內容。
示例2:顯示Xiaoyu的姓氏和ID號碼
[root@chensiqi1 files]#cat reg.txt
Zhang Dandan 41117397 :250:100:175
Zhang Xiaoyu 390320151 :155:90:201
Meng Feixue 80042789 :250:60:50
Wu Waiwai 70271111 :250:80:75
Liu Bingbing 41117483 :250:100:175
Wang Xiaoai 3515064655 :50:95:135
Zi Gege 1986787350 :250:168:200
Li Youjiu 918391635 :175:75:300
Lao Nanhai 918391635 :250:100:175
[root@chensiqi1 files]# awk -F "[ :]+" '$2~/^Xiaoyu/{print $1,$3}' reg.txt
Zhang 390320151
命令說明:
指定分隔符-F “【:】+”
$2~/Xiaoyu/表示條件,第二列包含Xiaoyu時候執行對應的動作
{print $1,$3}表示動作,顯示第一列和第三列的內容
示例3:顯示所有以41開頭的ID號碼的人的全名和ID號碼
[root@chensiqi1 files]# cat reg.txt
Zhang Dandan 41117397 :250:100:175
Zhang Xiaoyu 390320151 :155:90:201
Meng Feixue 80042789 :250:60:50
Wu Waiwai 70271111 :250:80:75
Liu Bingbing 41117483 :250:100:175
Wang Xiaoai 3515064655 :50:95:135
Zi Gege 1986787350 :250:168:200
Li Youjiu 918391635 :175:75:300
Lao Nanhai 918391635 :250:100:175
[root@chensiqi1 files]# awk -F "[ :]+" '$3~/^(41)/{print $1,$2,$3}' reg.txt
Zhang Dandan 41117397
Liu Bingbing 41117483
示例4:顯示所有以一個D或X開頭的人名全名
[root@chensiqi1 files]# cat reg.txt
Zhang Dandan 41117397 :250:100:175
Zhang Xiaoyu 390320151 :155:90:201
Meng Feixue 80042789 :250:60:50
Wu Waiwai 70271111 :250:80:75
Liu Bingbing 41117483 :250:100:175
Wang Xiaoai 3515064655 :50:95:135
Zi Gege 1986787350 :250:168:200
Li Youjiu 918391635 :175:75:300
Lao Nanhai 918391635 :250:100:175
[root@chensiqi1 files]# awk -F "[ :]+" '$2~/^D|^X/{print $1,$2}' reg.txt
Zhang Dandan
Zhang Xiaoyu
Wang Xiaoai
命令說明:
-F “【 :】+”指定分隔符
|表示或,^以...開頭
注意:
這里要用()括號表示即(D|X)相當於D|X,有的同學寫成D|X這樣是錯誤的。
示例5:顯示所有ID號碼最后一位數字是1或5的人的全名
[root@chensiqi1 files]# cat reg.txt
Zhang Dandan 41117397 :250:100:175
Zhang Xiaoyu 390320151 :155:90:201
Meng Feixue 80042789 :250:60:50
Wu Waiwai 70271111 :250:80:75
Liu Bingbing 41117483 :250:100:175
Wang Xiaoai 3515064655 :50:95:135
Zi Gege 1986787350 :250:168:200
Li Youjiu 918391635 :175:75:300
Lao Nanhai 918391635 :250:100:175
[root@chensiqi1 files]# awk -F "[ :]+" '$3~/1$|5$/{print $1,$2}' reg.txt
Zhang Xiaoyu
Wu Waiwai
Wang Xiaoai
Li Youjiu
Lao Nanhai
示例6:顯示Xiaoyu的捐款,每個值都有以$開頭。如$520$200$135
[root@chensiqi1 files]# cat reg.txt
Zhang Dandan 41117397 :250:100:175
Zhang Xiaoyu 390320151 :155:90:201
Meng Feixue 80042789 :250:60:50
Wu Waiwai 70271111 :250:80:75
Liu Bingbing 41117483 :250:100:175
Wang Xiaoai 3515064655 :50:95:135
Zi Gege 1986787350 :250:168:200
Li Youjiu 918391635 :175:75:300
Lao Nanhai 918391635 :250:100:175
[root@chensiqi1 files]# awk -F "[ :]+" '$2~/Xiaoyu/{print "$"$4"$"$5"$"$6}' reg.txt
$155$90$201
示例7:顯示所有人的全名,以姓,名的格式顯示,如Meng,Feixue
[root@chensiqi1 files]# cat reg.txt
Zhang Dandan 41117397 :250:100:175
Zhang Xiaoyu 390320151 :155:90:201
Meng Feixue 80042789 :250:60:50
Wu Waiwai 70271111 :250:80:75
Liu Bingbing 41117483 :250:100:175
Wang Xiaoai 3515064655 :50:95:135
Zi Gege 1986787350 :250:168:200
Li Youjiu 918391635 :175:75:300
Lao Nanhai 918391635 :250:100:175
[root@chensiqi1 files]# awk -F "[ ]+" '{print $1","$2}' reg.txt
Zhang,Dandan
Zhang,Xiaoyu
Meng,Feixue
Wu,Waiwai
Liu,Bingbing
Wang,Xiaoai
Zi,Gege
Li,Youjiu
Lao,Nanhai
2.2.9 企業面試題:取出網卡eth0的ip地址
最簡單:hostname -I
awk處理:
方法一:
[root@chensiqi1 files]# ifconfig eth0|awk 'BEGIN{RS="[ :]"}NR==31'
192.168.197.133
方法二:
[root@chensiqi1 files]# ifconfig eth0 | awk -F "(addr:)|( Bcast:)" 'NR==2{print $2}'
192.168.197.133
方法三:
[root@chensiqi1 files]# ifconfig eth0 | awk -F "[ :]+" 'NR==2{print $4}'
192.168.197.133
方法四:
[root@chensiqi1 files]# ifconfig eth0 | awk -F "[^0-9.]+" 'NR==2{print $2}'
192.168.197.133
提示:
- 前邊的三種方法都還是比較好理解的,這第四種方法,需要學會逆向思維,看看我們要的結果10.0.0.50,ip地址:數字和點(.),我是否可以指定分隔符,以數字和點以外的字符為分隔符呢?
- 換句話說就是要排除數字和點(.)正則表達式與排除常用的就是[^0-9.]即不匹配數字和點(.)
- 最后-F “[^0-9]”位分隔符,但是要使用+,表示連續的。合起來就是:awk -F “[^0-9.]+” 'NR==2{print $2}'
####2.2.10 明明白白擴展正則表達式:+(加號)注意:
正則表達式是玩好awk的必要條件,必會掌握
[root@chensiqi1 files]# echo "------======1########2"
------======1########2
[root@chensiqi1 files]# echo "------======1########2" | grep "[-=#]"
------======1########2
[root@chensiqi1 files]# echo "------======1########2" | grep -o "[-=#]"
-
-
-
-
-
-
=
=
=
=
=
=
#
#
#
#
#
#
#
#
####2.2.11 awk正則之{} -花括號
awk中的花括號有些不常用,但是偶爾會用到這里簡單介紹。
示例:取出awkfile中第一列包含一個o或者兩個o的行
[root@chensiqi1 files]# awk -F: '$1~/o{1,2}/' awkfile.txt
[root@chensiqi1 files]# awk -F: --posix '$1~/o{1,2}/' awkfile.txt
root:x:0:0:root:/root:/bin/bash
daemon:x:2:2:daemon:/sbin:/sbin/nologin
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
[root@chensiqi1 files]# awk -F: --re-interval '$1~/o{1,2}/' awkfile.txt
root:x:0:0:root:/root:/bin/bash
daemon:x:2:2:daemon:/sbin:/sbin/nologin
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
####2.2.12 企業案例1:取出常用服務端口號 思路: linux下面服務與端口信息的對應表格在/etc/services里面,所以這道題要處理/etc/services文件。
我們簡單分析以下servics文件:
[root@chensiqi1 ~]# sed -n '23,30p' /etc/services
tcpmux 1/tcp # TCP port service multiplexer
tcpmux 1/udp # TCP port service multiplexer
rje 5/tcp # Remote Job Entry
rje 5/udp # Remote Job Entry
echo 7/tcp
echo 7/udp
discard 9/tcp sink null
discard 9/udp sink null
從23行開始基本上每一行第一列是服務名稱,第二列的第一部分是端口號,第二列的第二部分是tcp或udp協議。
方法:
[root@chensiqi1 ~]# awk -F "[ /]+" '$1~/^(ssh)$|^(http)$|^(https)$|^(mysql)$|^(ftp)$/{print $1,$2}' /etc/services |sort|uniq
ftp 21
http 80
https 443
mysql 3306
ssh 22
提示:
- |是或者的意思,正則表達式
- sort是將輸出結果排序
- uniq是去重復但不標記重復個數
- uniq -c去重復但標記重復個數
###2.3 比較表達式做為模式-需要一些例子同學們自己嘗試下
之前我們看了正則表達式在awk下的運用,下面再具體看看比較表達式如何在awk下工作。
awk是一種編程語言,能夠進行更為復雜的判斷,當條件為真時候,awk就執行相關的action。主要是針對某一區域做出相關的判斷,比如打印成績在80分以上的行,這樣就必須對這一區域做比較判斷,下表列出了awk可以使用的關系運算符,可以用來比較數字字符串,還有正則表達式。當表達式為真時候,表達式結果1,否0,只有表達式為真,awk才執行相關的action
| 運算符 | 含義 | 示例 |
|---|---|---|
| < | 小於 | x>y |
| <= | 小於等於 | x<=y |
| == | 等於 | x==y |
| != | 不等於 | x!=y |
| >= | 大於或等於 | x>=y |
| > | 大於 | x<y |
以上運算符是針對數字的,下面兩個運算符之前已有示例,針對字符串
| ~ | 與正則表達式匹配 | x~/y/ |
|---|---|---|
| !~ | 與正則表達式不匹配 | x!~y |
思路:
想表示一個范圍,一個行的范圍,就要用到NR這個內置變量了,同時也要用到比較表達式。
答案:
[root@www ~]# awk 'NR>=23&&NR<=30' /etc/services
[root@www ~]# awk 'NR>22&&NR<31' /etc/services
過程:
[root@www ~]# awk 'NR>=23&&NR<=30' /etc/services
tcpmux 1/tcp # TCP port service multiplexer
tcpmux 1/udp # TCP port service multiplexer
rje 5/tcp # Remote Job Entry
rje 5/udp # Remote Job Entry
echo 7/tcp
echo 7/udp
discard 9/tcp sink null
discard 9/udp sink null
[root@www ~]# awk 'NR>22&&NR<31' /etc/services
tcpmux 1/tcp # TCP port service multiplexer
tcpmux 1/udp # TCP port service multiplexer
rje 5/tcp # Remote Job Entry
rje 5/udp # Remote Job Entry
echo 7/tcp
echo 7/udp
discard 9/tcp sink null
discard 9/udp sink null
說明:
1)比較表達式比較常用的還是表示大於等於,小於等於或者等於,根據這個例子來學習即可
2)NR表示行號,大於等於23即,NR>=23小於等於30,即NR<=30
3)合起來就是NR>=23並且NR<=30,&&表示並且,同時成立的意思。
4)換一種表達式方法就是大於22行小於31行,即NR>22&&NR<31
示例:找出/etc/passwd中第五列是root的行
測試文件:
[root@www ~]# cat /server/files/awk_equal.txt
root:x:0:0:root:/root:/bin/bash
root:x:0:0:rootroot:/root:/bin/bash
root:x:0:0:rootrooot:/root:/bin/bash
root:x:0:0:rootrooot:/root:/bin/bash
root:x:0:0:/root:/bin/bash
答案:
awk -F":" '$5=="root"' /server/files/awk_equal.txt
awk -F":" '$5~/^root$/' /server/files/awk_equal.txt
過程:
#方法一:
[root@www ~]# awk -F":" '$5=="root"' /server/files/awk_equal.txt
root:x:0:0:root:/root:/bin/bash
#方法二:
[root@www ~]# awk -F":" '$5~/^root$/' /server/files/awk_equal.txt
root:x:0:0:root:/root:/bin/bash
我們如果想要完全匹配root這個字符串,那就用$5=="root"即可,這也是答案里面給大家的。
方法二:
此題也可通過正則匹配來限制root的字符串。$5~/^root$/
- 范圍模式簡單理解就是從哪里來,到哪里去。
- 匹配從條件1開始到條件2介紹的范圍
1)還記得sed使用地址范圍來處理文本內容嘛?awk的范圍模式,與sed類似,但是又有不同,awk不能直接使用行號來作為范圍起始地址,因為awk具有內置變量NR來存儲記錄號,所有需要使用NR=1,NR=5這樣來使用。
2)范圍模式處理的原則是:先匹配從第一個模式的首次出現到第二個模式的首次出現之間的內容,執行action。然后匹配從第一個模式的下一次出現到第二個模式的下一次出現,直到文本結束。如果匹配到第一個模式而沒有匹配到第二個模式,則awk處理從第一個模式開始直到文本結束全部的行。如果第一個模式不匹配,就算第二個模式匹配,awk依舊不處理任何行。
awk '/start pos/,/end pos/{print $)} passwd chensiqi'
awk '/start pos/,NR==XXX{print $0}' passwd chensiqi
范圍模式的時候,范圍條件的時候,表達式必須能匹配一行。
示例1:
[root@www files]# awk 'NR==2,NR==5{print NR,$0}' count.txt
2 bin x bin bin sbin nologin
3 daemon x daemon sbin sbin nologin
4 adm x adm var adm sbin nologin
5 lp x lp var spool lpd sbin nologin
說明:
條件是:從第二行,到第五行
動作是:顯示行號(NR)和整行($0)
合起來就是顯示第二行到第五行的行好和整行的內容
示例2:
[root@www files]# awk '/^bin/,NR==5{print NR,$0}' awkfile.txt
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
說明:
條件是:從以bin開頭的行,到第五行
動作是:顯示行號和整行內容
合起來就是顯示從以bin開頭的行,到第五行中的行號和整行內容。
示例3:
[root@www files]# awk -F":" '$5~/^bin/,/^lp/{print NR,$0}' awkfile.txt
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
說明:
條件:從第五列以bin開頭的行到以lp開頭的行
動作:顯示行號和正航內容
合起來:從第三列以bin開始的行到以lp開頭的行並顯示其行號和整行內容
[root@www files]# awk -F: '$5~/^bin/,$5~/^lp/{print NR,$0}' awkfile.txt
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
說明:
條件:從第三列以bin開頭字符串的行到第三列以lp開頭字符串的行
動作:顯示行號和整行
- BEGIN模塊再awk讀取文件之前就執行,一般用來定義我們的內置變量(預定義變量,eg:FS,RS),可以輸出表頭(類似excel表格名稱)
- BEGIN模式之前我們有在示例中提到,自定義變量,給內容變量賦值等,都使用過。需要注意的是BEGIN模式后面要接跟一個action操作塊,包含在大括號內。awk必須在輸入文件進行任何處理前先執行BEGIN里的動作(action)。我們可以不要任何輸入文件,就可以對BEGIN模塊進行測試,因為awk需要先執行完BEGIN模式,才對輸入文件做處理。BEGIN模式常常被用來修改內置變量ORS,RS,FS,OFS等值。
1)第一個作用,內置變量的定義
示例:取eth0的IP地址
答案:
[root@www files]# ifconfig eth0|awk -F "(addr:)|( Bcast:)" 'NR==2{print $2}'
192.168.197.133
[root@www files]# ifconfig eth0 | awk -F "[ :]+" 'NR==2{print $4}'
192.168.197.133
[root@www files]# ifconfig eth0 | awk -F "[^0-9.]+" 'NR==2{print $2}'
192.168.197.133
#上面的也可以寫成
[root@www files]# ifconfig eth0 | awk 'BEGIN{FS="(addr:)|( Bcast:)"} NR==2{print $2}'
192.168.197.133
[root@www files]# ifconfig eth0 | awk 'BEGIN{FS="[ :]+"}NR==2{print $4}'
192.168.197.133
[root@www files]# ifconfig eth0 | awk 'BEGIN{FS="[^0-9.]+"}NR==2{print $2}'
192.168.197.133
注意:
命令行-F本質就是修改的FS變量
2)第二個作用,在讀取文件之前,輸出些提示性信息(表頭)。
[root@www files]# awk -F: 'BEGIN{print "username","UID"}{print $1,$3}' awkfile.txt
username UID #這就是輸出的表頭信息
root 0
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
halt 7
mail 8
uucp 10
說明:
要在第一行輸出一些username和UID,我們應該想到BEGIN{}這個特殊的條件(模式),因為BEGIN{}在awk讀取文件之前執行的。
所以結果是BEGIN{print "username","UID"},注意print命令里面雙引號吃啥吐啥,原樣輸出。
然后我們實現了在輸出文件內容之前輸出“username”和“UID”,下一步輸出文件的第一列和第三列即{print $1,$3}
最后結果就是BEGIN{print "username","UID"}{print $1,$3}
3)第三個作用,使用BEGIN模塊的特殊性質,進行一些測試。
[root@www files]#簡單輸出內容:
[root@www files]# awk 'BEGIN{print "hello world!"}'
hello world!
[root@www files]# #進行計算
[root@www files]# awk 'BEGIN{print 10/3}'
3.33333
[root@www files]# awk 'BEGIN{print 10/3+1}'
4.33333
[root@www files]# awk 'BEGIN{print 10/3+1/4*9}'
5.58333
[root@www files]# #和變量有關的操作
[root@www files]# awk 'BEGIN{a=1;b=2;print a,b}'
1 2
[root@www files]# awk 'BEGIN{a=1;b=2;print a,b,a+b}'
1 2 3
4)第四種用法:配合getline讀取文件,后面awk函數處講解
####2.5.2 awk中變量的概念簡介- 直接定義,直接使用即可
- awk中字母會被認為是變量,如果真的要給一個變量賦值字母(字符串),請使用雙引號
[root@chensiqi files]# awk 'BEGIN{a=abcd;print a}'
[root@chensiqi files]# awk 'BEGIN{abcd=123456;a=abcd;print a}'
123456
[root@chensiqi files]# awk 'BEGIN{a="abcd";print a}'
abcd
說明:
沒有文件awk依舊可以處理BEGIN模式下的動作(命令)
EHD在awk讀取完所有的文件的時候,再執行END模塊,一般用來輸出一個結果(累加,數組結果),也可以是和BEGIN模塊類似的結尾標識信息
[root@chensiqi files]# awk 'BEGIN{print "hello world!"}{print NR,$0}END{print "end of file"}' count.txt
hello world!
1 root x root root bin bash
2 bin x bin bin sbin nologin
3 daemon x daemon sbin sbin nologin
4 adm x adm var adm sbin nologin
5 lp x lp var spool lpd sbin nologin
6 sync x sync sbin bin sync
7 shutdown x shutdown sbin sbin shutdown
8 halt x halt sbin sbin halt
9 mail x mail var spool mail sbin nologin
10 uucp x uucp var spool uucp sbin nologin
end of file
與BEGIN模式相對應的END模式,格式一樣,但是END模式僅在awk處理完所有輸入行后才進行處理。
企業案例:統計/etc/servies文件里的空行數量
思路:
a)空行通過正則表達式來實現:^$
b)統計數量:
- grep -c
- awk
方法一:grep
[root@chensiqi files]# grep "^$" /etc/services | wc -l
16
[root@chensiqi files]# grep -c "^$" /etc/services
16
說明:
grep命令-c表示count計數統計包含^$的行一共有多少。
方法二:
[root@chensiqi files]# awk '/^$/{i++}END{print i}' /etc/services
16
提示:
使用了awk的技術功能,很常用
第一步:統計空行個數
/^$/表示條件,匹配出空行,然后執行{i++}(i++等於i=i+1)即:/^$/{i=i+1}
我們可以通過/^$/{i=i+1;print i}來查看awk執行過程
[root@chensiqi files]# awk '/^$/{i=i+1;print "the value of i is:"i}' /etc/services
the value of i is:1
the value of i is:2
the value of i is:3
the value of i is:4
the value of i is:5
the value of i is:6
the value of i is:7
the value of i is:8
the value of i is:9
the value of i is:10
the value of i is:11
the value of i is:12
the value of i is:13
the value of i is:14
the value of i is:15
the value of i is:16
第二步:輸出最后結果
- 但是我們只想要最后的結果16,不想要過程怎么辦?使用END模式輸出結果
- 因為END模式的特殊性質所以很適合輸出最終結果
所以最終結果就是awk '/^$/{i=i+1}END{print "blank lines count:"i}' /etc/services
awk編程思想:
- 先處理,最后再END模塊輸出
{print NR,$0}body模塊處理,處理完畢后END{print "end of file"}輸出一個結果
企業面試題5:文件count.txt,文件內容是1到100(由seq 100生成),請計算文件每行值加起來的結果(計算1+...+100)
思路:
文件每一行都有且只有一個數字,所以我們要讓文件的每行內容相加。
回顧一下上一道題我們用的是i++即i=i+1
這里我們需要使用到第二個常用的表達式
i=i+$0
對比一下,其實只是把上邊的1換成了$0
[root@chensiqi files]# awk '{i=i+$0}END{print i}' count.txt
5050
###2.6 awk中的動作
在一個模式-動作語句中,模式決定動作什么時候執行,有時候動作會非常簡單:一條單獨的打印或賦值語句。在有些時候,動作有可能是多條語句,語句之間用換行符或分號分開。
awk的動作中如果有兩個或兩個以上的語句,需要用分號分隔
動作部分大家理解為花括號里面的內容即可,總體分為:
- 表達式
- 流程控制語句
- 空語句
- 數組(以后如果有時間的話會再寫一個awk高級部分進行介紹)
- awk命令核心由模式和動作組成
- 模式就是條件,動作就是具體干什么
1)正則表達式:必須掌握正則,熟練
2)條件表達式:比大小,比較是否相等
3)范圍表達式:從哪里來到哪里去 - 注意BEGIN或END模塊只能有一個。BEGIN{}BEGIN{}或者END{}END{}都是錯誤的。
awk -F 指定分隔符 ‘BRGIN{}END{}’,如下圖

#awk完整執行過程
[root@chensiqi ~]# awk -F ":" 'BEGIN{RS="/";print "hello world!"}{print NR,$0}END{print "end of file"}' /server/files/awkfile.txt
hello world!
1 root:x:0:0:root:
2 root:
3 bin
4 bash
bin:x:1:1:bin:
5 bin:
6 sbin
7 nologin
daemon:x:2:2:daemon:
8 sbin:
9 sbin
10 nologin
adm:x:3:4:adm:
11 var
12 adm:
13 sbin
14 nologin
lp:x:4:7:lp:
15 var
16 spool
17 lpd:
18 sbin
19 nologin
sync:x:5:0:sync:
20 sbin:
21 bin
22 sync
shutdown:x:6:0:shutdown:
23 sbin:
24 sbin
25 shutdown
halt:x:7:0:halt:
26 sbin:
27 sbin
28 halt
mail:x:8:12:mail:
29 var
30 spool
31 mail:
32 sbin
33 nologin
uucp:x:10:14:uucp:
34 var
35 spool
36 uucp:
37 sbin
38 nologin
end of file
說明:
我們·同時再命令行定義了分隔符和在BEGIN模式中定義了RS內置變量,在最后通過END模式輸出了結果
awk提供了數組來存放一組相關的值。
awk是一種編程語言,肯定也支持數組的運用,但是又不同於c語言的數組。數組在awk中被稱為關聯數組,因為它的下標既可以是數字也可以是字符串。下標通常被稱作key,並且與對應的數組元素的值關聯。數組元素的key和值都存儲在awk程序內部的一張表中,通過一定散列算法來存儲,所以數組元素都不是按順序存儲的。打印出來的順序也肯定不是按照一定的順序,但是我們可以通過管道來對所需的數據再次操作來達到自己的效果。

如圖不難發現,awk數組就和酒店一樣。數組的名稱就像是酒店名稱,數組元素名稱就像酒店房間號碼,每個數組元素里面的內容就像是酒店房間里面的人。
###2.10 圖片-數組假設我們有一個酒店
酒店<===>chensiqihotel
酒店里面有幾個房間110,119,120,114這幾個房間
酒店110房間<===>chensiqihotel[110]
酒店120房間<===>chensiqihotel[120]
酒店119房間<===>chensiqihotel[119]
酒店114房間<===>chensiqihotel[114]
酒店房間里面入住客人
酒店110房間住着xiaoyu<===>chensiqihotel[110]="xiaoyu"
酒店119房間住着ruxue<===>chensiqihotel[119]="ruxue"
酒店120房間住着dandan<===>chensiqihotel[120]="dandan"
酒店114房間住着waiwai<===>chensiqihotel[114]="waiwai"
示例:
[root@chensiqi ~]# awk 'BEGIN{chensiqihotel[110]="xiaoyu";chensiqihotel[119]="ruxue";chensiqihotel[120]="dandan";chensiqihotel[114]="waiwai";print chensiqihotel[110],chensiqihotel[119],chensiqihotel[120],chensiqihotel[114]}'
xiaoyu ruxue dandan waiwai
[root@chensiqi ~]# awk 'BEGIN{chensiqihotel[110]="xiaoyu";chensiqihotel[119]="ruxue";chensiqihotel[120]="dandan";chensiqihotel[114]="waiwai";for(hotel in chensiqihotel)print hotel,chensiqihotel[hotel]}'
110 xiaoyu
120 dandan
114 waiwai
119 ruxue
企業面試題1:統計域名訪問次數
處理以下文件內容,將域名取出並根據域名進行計數排序處理:(百度和sohu面試題)
http://www.etiantian.org/index.html
http://www.etiantian.org/1.html
http://post.etiantian.org/index.html
http://mp3.etiantian.org/index.html
http://www.etiantian.org/3.html
http://post.etiantian.org/2.html
思路:
1)以斜線為菜刀取出第二列(域名)
2)創建一個數組
3)把第二列(域名)作為數組的下標
4)通過類似於i++的形式進行計數
5)統計后把結果輸出
過程演示:
第一步:查看一下內容
[root@chensiqi ~]# awk -F "[/]+" '{print $2}' file
www.etiantian.org
www.etiantian.org
post.etiantian.org
mp3.etiantian.org
www.etiantian.org
post.etiantian.org
命令說明:
這是我們需要計數的內容
第二步:計數
[root@chensiqi ~]# awk -F "[/]+" '{i++;print $2,i}' file
www.etiantian.org 1
www.etiantian.org 2
post.etiantian.org 3
mp3.etiantian.org 4
www.etiantian.org 5
post.etiantian.org 6
命令說明:
i++:i最開始是空的,當awk讀取一行,i自身+1
第三步:用數組替換i
[root@chensiqi ~]# awk -F "[/]+" '{h[$2]++;print $2,h["www.etiantian.org"]}' file
www.etiantian.org 1
www.etiantian.org 2
post.etiantian.org 2
mp3.etiantian.org 2
www.etiantian.org 3
post.etiantian.org 3
命令說明:
1)將i替換成h[$2];相當於我創建了一個數組h[],然后用$2作為我的房間號。但是目前房間里是沒有東西的。也就是說h[$2]=h["www.etiantian.org"] and h["post.etiantian.org"] and h["mp3.etiantian.org"] 但是具體房間里是沒有東西的也就是空。
2)h[$2]++就等於i++:也就是說我開始給房間里加東西;當出現同樣的東西,我就++
3)print h["www.etiantian.org"]:意思就是說我開始要輸出了。我要輸出的是房間號為“www.etiantian.org”里面的內容。這里面的內容最早是空的,隨着awk讀取每一行一旦出現房間號為“www.etiantian.org”的房間時,我就給房間里的內容進行++。
4)綜上,輸出的結果中,每次出現www.etiantian.org時,h["www.etiantian.org"]就會++。因此最后的輸出數字是3
第四步:輸出最終計數結果
[root@chensiqi ~]# awk -F "[/]+" '{h[$2]++}END{for(i in h)print i,h[i]}' file
mp3.etiantian.org 1
post.etiantian.org 2
www.etiantian.org 3
[root@chensiqi ~]#
命令說明:
我們最終需要輸出的是去重復以后的統計結果,所以得在END模塊里進行輸出
for(i in h)遍歷這個數組,i里存的都是房間號
print i,h[i]:輸出每一個房間號及其房間里的內容(計數結果)
提示:
awk的應用里最重要的一個功能就是計數,而數組在awk里最大的作用就是去重復。請同學們仔細理解,多動手試驗一下。
abc ↩︎
