shell 正則表達式

 

知識內容:
* 定義正則表達式
* 基礎知識
* 擴展模式
* 創建表達式
在shell腳本中成功地使用sed編輯器和gawk程序的關鍵是自如地使用正則表達式,設法從大批數據中篩選特定的數據非常複雜。所以說,這是一個難點!
1、正則表達式
1.1、正則表達式的定義
linux使用程序在輸入數據時,將正則表達式模式和數據進行匹配,如果數據與模式一致就接受處理。如果數據與模式不一致,它就拒絕處理。像*通配符在匹配linux程序就是最簡單的正則表達式應用了。
1.2、正則表達式的類型
在linux領域,幾個不同的應用程序要使用不同類型的正則表達式。這些不同的應用程序包括編程語言(Java、Perl以及Python)、linux實用程序(如sed編輯器、gawk程序、grep實用程序),以及主流應用程序(如MYSQL和PostgreSQL數據庫服務器程序)
正則表達式可以使用正則表達式引擎實現,正則表達式引擎是解釋正則表達式並使用這些模式匹配文本的基礎軟件。在linux領域,常用的正則表達式引擎有兩種:
* POSIX基本正則表達式(BRE)引擎
* POSIX擴展正則表達式(ERE)引擎
大多數的linux使用程序至少要符合POSIX BRE引擎規範,能夠識別其定義的所有模式符號。
下面就講述最常見的正則表達式,並說明如何在sed編輯器和gawk程序中使用這些正則表達式。

2、定義BRE模式
最基本的BRE模式是匹配數據流中的文本字符。
2.1、純文本
先回顧一下sed和gawk下文本匹配的模式:
[root@wzp ~]# echo "welcome to 51cto" | sed -n '/51cto/p' 
//-n表示把匹配的文本輸出到STDOUT,其他的任何文本都不輸出到STDOUT
welcome to 51cto
[root@wzp ~]# echo "welcome to 51cto" | gawk '/51cto/{print $0}'
welcome to 51cto
正則表達式關鍵是匹配與數據流中的文本,而不在乎文本出現在數據流中的位置,出現的次數也無關緊要。
對於大小寫的匹配也是很嚴格:
[root@wzp ~]# echo "Welcome to CHINA" | sed -n '/welcome/p'
[root@wzp ~]# echo "Welcome to CHINA" | sed -n '/Welcome/p'
Welcome to CHINA
還有就是不必侷限於文本的完整性問題,所定義的文本出現在數據流的任意位置都可以匹配:
[root@wzp ~]# echo "the book is expensive" | sed -n '/books/p'
[root@wzp ~]# echo "the books are expensive" | sed -n '/book/p'
the books are expensive
儘管數據流中的文本是books,但數據流中的數據包含正則表達式book就可以打印出來;反之,定義的正則表達式books(文本中只有book,沒有books)並沒有能夠匹配的數據流,因此匹配失敗而無法打印。
還有就是先前已經演示過的,在正則表達式的匹配中不必侷限單個單詞,也可以在文本字符串中包含空格和數據:
[root@wzp ~]# echo "this is number 1" | sed -n '/ber 1/p'
this is number 1
如上在正則表達式中已經定義了一個空格,它就必須出現在數據流中
2.2、特殊字符
正則表達式認可的特殊字符有:
. * [ ] ^ $ { } \ + ? | ( )
如果要在文本模式中使用他們就必須使用反斜槓做一個轉義(\),例如要在文本中匹配美元符號:
[root@wzp ~]# cat file
this costs $24.0
[root@wzp ~]# sed -n '/\$/p' file
this costs $24.0
對於反斜槓的使用也必須轉義才能使得正則表達式匹配數據流:
[root@wzp ~]# echo "\ is a special character" | sed -n '/\\/p'
\ is a special character
最後還有點注意的是雖然正斜槓(/)不是正則表達式的特殊字符,但如果在sed編輯器或gawk程序的正則表達式模式中使用也需要轉義的:
[root@wzp ~]# echo "/ is also a special character" | sed -n '///p'
sed: -e expression #1, char 3: unknown command: `/'
[root@wzp ~]# echo "/ is also a special character" | sed -n '/\//p'
/ is also a special character
2.3、定位符
上面講述的在默認情況下,在指定正則表達式模式時,只要模式出現在數據流之中就可以匹配。
這裏頭的兩個特殊字符可以用來將模式定位到數據流行的開頭或結尾。
2.3.1、從頭符始
開脫字(^)定義從數據流中文本行開頭開始的模式,如果該模式位於文本行的其他任意位置則無法匹配了!
[root@wzp ~]# echo "the book is great" |sed -n '/^book/p'
[root@wzp ~]# echo "the book is great" |sed -n '/^the/p'
the book is great
脫字符定義了文本行開頭必須是the(由換行符確定),否則定義的正則表達式就沒有能夠匹配的數據流了。
[root@wzp ~]# cat file2
That is great
THAT is great
that is great
yeah, that is great
[root@wzp ~]# sed -n '/^that/p' file2
that is great
如上同樣的理解,依舊是匹配that開頭的文本行。
但這裏有點需要注意的是,如果脫字符放在模式的其他位置,它就充當普通字符而不再是特殊字符了:
[root@wzp ~]# echo "this ^ is a test" | sed -n '/^/p'
this ^ is a test
把文本中含有脫字符的打印出來,則出現了正則表達式得以匹配的數據流而輸出到STDOUT。
2.3.2、查找結尾
跟脫字符類似的,如果要找跟行尾模式匹配的文本,通過美元符號($)即可定位:
[root@wzp ~]# echo "welcome to IT website 51cto.com" | sed -n '/.com.cn$/p'
[root@wzp ~]# echo "welcome to IT website 51cto.com" | sed -n '/.com$/p'
welcome to IT website 51cto.com
從上面結果就得知最後以.com結尾的文本能夠使得正則表達式匹配到數據流而echo出來。
2.3.3、聯合定位
聯合定位表示說同時使用脫字符和美元符號來實現數據流的匹配,最典型的就是去除文本中的空行:
[root@wzp ~]# cat file3
this is a test line

that is empty above
thai is empty too below

test is over !
現在可以看到上面的文本出現兩行空行,這裏就可以採用d來把這兩行delete掉,如下:
[root@wzp ~]# sed  '/^$/d' file3
this is a test line
that is empty above
thai is empty too below
test is over !
如上所定義的正則表達式模式只查找行頭和行尾之間沒有內容的行,然後delete掉,剩下的內容就輸出到STDOUT,注意這裏就不要使用-n選項了,不然輸出則爲空了。
所以這是一個非常好的方法提煉出某下服務配置文件的主題內容,比如說提取出Nginx的主配置文件初始內容,我們知道該配置文件存在很多#註釋行,我們也需要把這些行去掉:
[root@wzp ~]# cat /usr/local/nginx/conf/nginx.conf | grep -v "#" | sed '/^$/d'
worker_processes  4;
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    access_log  logs/access.log main buffer=1k;
    sendfile        on;
    keepalive_timeout  65;
    gzip  on;
........省略掉........
由此可見聯合定位是一種很不錯的方法。記得別使用-n選項,否則顯示爲空!
2.3.4、點字符
點字符用於匹配除換行符之外的任何單個字符,但點字符必須匹配一個字符。先看個例子:
[root@wzp ~]# cat file4
at home
this is a test line
the cat is sleeping
that is a nice hat
the game is over at ten o'clock
[root@wzp ~]# sed -n '/.at/p' file4
the cat is sleeping
that is a nice hat
the game is over at ten o'clock
這裏應該能理解at home爲何無法匹配,因爲前面沒有任何字符,而其他行中都包含at這個文本字符串,並且at前面還存在至少一個字符(哪怕爲空格也被視爲一個字符),所以才得以顯示。
2.3.5、字符類
點字符適合匹配某一字符位置上的任意字符,但如果要限制匹配的字符則需要使用字符類。
首先來看一個例子在說說它的概念吧,同樣採用上面的file4:
[root@wzp ~]# cat file4
at home
this is a test line
the cat is sleeping
that is a nice hat
the game is over at ten o'clock
[root@wzp ~]# sed -n '/[ch]at/p' file4
the cat is sleeping
that is a nice hat
這裏頭的[ch]at表示匹配模式爲cat和hat,只有文本中出現這兩種類型的文本之一就得以匹配。
回頭來說說字符類的概念,它是通過使用方括號,然後把定義的字符類視爲一個整體,跟其他通配符一樣。
如上的例子跟點字符不同在於僅僅出現at(哪怕前面至少有一個字符)也是不匹配的,必須前面出現c或者h才行。所以這點實際上也不難理解,習慣性使用它就OK了。再來看看一個例子,都很好理解:
[root@wzp ~]# echo "Yes" | sed -n '/[Yy]es/p'
Yes
[root@wzp ~]# echo "yes" | sed -n '/[Yy]es/p'
yes
通過這種匹配模式,我們就可以來判斷一下手機號碼前三位了:
[root@wzp ~]# cat file5
135
150
1580
16
188
[root@wzp ~]# sed -n '/[1][0123456789][0123456789]/p' file5
135
150
1580
188
從結果來說,對於只有兩個字符的行匹配不上這是預料之中的,不過1580也匹配上了其實也不矛盾。
因爲只要匹配字符類的要求就行了,而不要求後面的內容。所以要僅僅匹配3個字符,可以通過聯合定位的方法來解決這個問題:
[root@wzp ~]# sed -n '/^[1][0123456789][0123456789]$/p' file5
135
150
188
在[]的左右添加^$使得文本匹配只有三個字符,則得到了我們想要的結果。
2.3.6、否定字符類
在正則表達式模式中,可以顛倒字符類的作用,查找不在該字符類中的字符,這裏只需要在字符類範圍的開頭添加脫字符(不是感嘆號!),看一個例子:
[root@wzp ~]# cat file4
at home
this is a test line
the cat is sleeping
that is a nice hat
the game is over at ten o'clock
[root@wzp ~]# sed -n '/[^h]at/p' file4
the cat is sleeping
the game is over at ten o'clock
通過否定字符,正則表達式模式可以匹配除了hat文本模式之外的任何字符,包括空格字符,但at前面必須之手一個字符,我們可以驗證一下:
[root@wzp ~]# cat file4
at home
at home
this is a test line
the cat is sleeping
that is a nice hat
the game is over at ten o'clock
[root@wzp ~]# sed -n '/[^h]at/p' file4
at home
the cat is sleeping
the game is over at ten o'clock
第二行的at home前面有一個空格,則通過sed匹配後顯示出了。實際上最後一行也算是一個例子了!
2.3.7、使用範圍
先前舉了一個匹配手機號碼前三位的例子,使用了從0到9的數據段顯得很冗長,實際上這裏頭可以通過使用短劃線符號在字符類中使用一系列字符範圍,還是舉這麼個例子:
[root@wzp ~]# sed -n '/^[1][0-9][0-9]$/p' file5
135
150
188
直接通過0-9來替代一連串的數據。同樣的方法也適合於字母a-z,看個例子:
[root@wzp ~]# cat file4
at home
at home
this is a test line
the cat is sleeping
that is a nice hat
the game is over at ten o'clock
[root@wzp ~]# sed -n '/[a-gk-z]at/p' file4
the cat is sleeping
對於模式[a-gk-z]表示從a到g和從k到z的字母範圍裏頭一個字母附帶at這樣的字符串出現在文本中,則正則表達式能夠匹配文本數據流而顯示。
[root@wzp ~]# sed -n '/[a-dg-z]at/p' file4
the cat is sleeping
that is a nice hat
2.3.8、特殊字符類
除了定義自己的字符類之外,BRE還包含特殊字符類,可以用於匹配特定類型的字符,如下表:
***********************BRE特殊字符類*******************************
    類                描述
[[:alpha:]]          匹配任意字母字符、大寫或者小寫
[[:alnum:]]          匹配任意字母數字字符,0~9,A~Z,a~z
[[:blank:]]          匹配空格或者製表符字符
[[:digit:]]          匹配0~9之間的數字
[[:lower:]]          匹配任意小寫字母字符,即a~z
[[:print:]]          匹配任意可打印字符
[[:punct:]]          匹配標點符號
[[:space:]]          匹配任意空白字符;空格、製表符、NL/FF/VT/CR
[[:upper:]]          匹配任意大寫字母字符,即A~Z
*******************************************************************
在正則表達式中,完全可以像使用普通字符類一樣使用特殊字符類:
[root@wzp ~]# echo "abc" |sed -n '/[[:digit:]]/p'
[root@wzp ~]# echo "abc" |sed -n '/[[:alpha:]]/p'
abc
[root@wzp ~]# echo "abc123" |sed -n '/[[:digit:]]/p'
abc123
通過對應BRE特殊字符表就可以理解數據的匹配模式了。
2.3.9、星號
在某個字符之後加一個星號(*)表示該字符必須在匹配模式的文本中不出現或出現多次:
[root@wzp ~]# echo "bk" | sed -n '/bo*k/p'
bk
[root@wzp ~]# echo "bok" | sed -n '/bo*k/p'
bok
[root@wzp ~]# echo "book" | sed -n '/bo*k/p'
book
[root@wzp ~]# echo "boooook" | sed -n '/bo*k/p'
boooook
從它的概念上九很好理解,不出現字符o,那麼bk依舊匹配文本數據流,出現字符o一次或者多次都匹配文本數據流。這裏還可以通過星號和使用範圍來實現更加廣泛的匹配模式:
[root@wzp ~]# echo "bt" | sed -n '/b[ea]*t/p'
bt
[root@wzp ~]# echo "bet" | sed -n '/b[ea]*t/p'
bet
[root@wzp ~]# echo "bat" | sed -n '/b[ea]*t/p'
bat
[root@wzp ~]# echo "beat" | sed -n '/b[ea]*t/p'
beat
[root@wzp ~]# echo "beaat" | sed -n '/b[ea]*t/p'
beaat
[root@wzp ~]# echo "bct" | sed -n '/b[ea]*t/p'
[root@wzp ~]# echo "bect" | sed -n '/b[ea]*t/p'
[root@wzp ~]# echo "baeeat" | sed -n '/b[ea]*t/p'
baeeat
只有e和a字符的組合出現在b和t之間(那怕完全不出現也匹配),模式就匹配。但如果出現其他字符,則匹配失敗。

2.4、擴展的正則表達式
POSIX ERE模式包括某些linux應用出現和工作使用的其他幾個符號。gawk程序能夠識別ERE模式,sed編輯器則不能!有點需要注意的是:sed編輯器和 gawk程序的正則表達式引擎存在差異。gawk程序可以使用大部分擴展正則表達式模式的符號,並且具有sed編輯器沒有的其他一些篩選能力。但是,也正是由於這點,它處理數據流的速度通常較慢。
2.4.1、問號
問號和星號相似,但不同的是問號(?)表示其前面的字符可以不出現或者出現一次,否則不匹配:
[root@wzp ~]# echo "bk" | gawk '/bo?k/{print $0}'
bk
[root@wzp ~]# echo "bok" | gawk '/bo?k/{print $0}'
bok
[root@wzp ~]# echo "book" | gawk '/bo?k/{print $0}'
如上顯示說明字符o要麼不出現、要麼僅能出現一次,正則表達式才能匹配文本數據流。
正如星號一樣,問號也可以使用範圍連用:
[root@wzp ~]# echo "bok" | gawk '/b[ao]?k/{print $0}'
bok
[root@wzp ~]# echo "bak" | gawk '/b[ao]?k/{print $0}'
bak
[root@wzp ~]# echo "bk" | gawk '/b[ao]?k/{print $0}'
bk
[root@wzp ~]# echo "baok" | gawk '/b[ao]?k/{print $0}'
要知道,同時出現ao是無法使得匹配文本數據流的,因爲問號(?)前面的字符最多出現一次,否則不匹配
2.4.2、加號
加號與星號相似,表示其前面的字符可以出現一次或多次(至少出現一次),則匹配模式:
[root@wzp ~]# echo "bk" | gawk '/bo+k/{print $0}'
[root@wzp ~]# echo "bok" | gawk '/bo+k/{print $0}'
bok
[root@wzp ~]# echo "boook" | gawk '/bo+k/{print $0}'
boook
對於字符類的匹配,作用方式跟星號和問號一樣:
[root@wzp ~]# echo "boook" | gawk '/b[oa]+k/{print $0}'
boook
[root@wzp ~]# echo "boaok" | gawk '/b[oa]+k/{print $0}'
boaok
[root@wzp ~]# echo "bk" | gawk '/b[oa]+k/{print $0}'
[root@wzp ~]# echo "bak" | gawk '/b[oa]+k/{print $0}'
bak
如上的顯示結果非常好理解,不用多說了。
2.4.3、使用大括號
在ERE中,可以使用大括號對可重複的正則表達式作限制,這通常叫做間隔,可以用兩種格式表示間隔:
* m: 該正則表達式正好出現m次
* m,n: 該正則表達式出現最少m次,最多n次
注意點:默認情況下,gawk程序不能識別正則表達式間隔,必須指定--re-interval命令行選項,以便gawk程序識別正則表達式間隔。
[root@wzp ~]# echo "bk" | gawk --re-interval '/bo{1}k/{print $0}'
[root@wzp ~]# echo "book" | gawk --re-interval '/bo{1}k/{print $0}'
[root@wzp ~]# echo "bok" | gawk --re-interval '/bo{1}k/{print $0}'
bok
通過指定間隔爲1,則必須僅僅出現bok才能得以匹配
對於指定上下限間隔的例子:
[root@wzp ~]# echo "booook" | gawk --re-interval '/bo{1,3}k/{print $0}'
[root@wzp ~]# echo "boook" | gawk --re-interval '/bo{1,3}k/{print $0}'
boook
[root@wzp ~]# echo "bok" | gawk --re-interval '/bo{1,3}k/{print $0}'
bok
凡是出現1~3個o的模式則匹配成功。當然還可以適用於字符類:
[root@wzp ~]# echo "bk" | gawk --re-interval '/b[oa]{1,3}k/{print $0}'
[root@wzp ~]# echo "bok" | gawk --re-interval '/b[oa]{1,3}k/{print $0}'
bok
[root@wzp ~]# echo "boaok" | gawk --re-interval '/b[oa]{1,3}k/{print $0}'
boaok
[root@wzp ~]# echo "boaoak" | gawk --re-interval '/b[oa]{1,3}k/{print $0}'
[root@wzp ~]# echo "baaak" | gawk --re-interval '/b[oa]{1,3}k/{print $0}'
baaak
對於顯示結果應該不用怎麼解釋了,跟前面的完全一樣。
2.4.4、管道符號
管道符號允許我們用邏輯or公式指定正則表達式檢查數據流時使用的兩個或多個模式,如果任何一個模式與數據流文本匹配,則文本通過。如果沒有一個匹配的,數據流文本匹配失敗。看個例子:
[root@wzp ~]# echo "the pig is sleeping" | gawk '/cat|dog/{print $0}'
[root@wzp ~]# echo "the dog is sleeping" | gawk '/cat|dog/{print $0}'
the dog is sleeping
[root@wzp ~]# echo "the cat is sleeping" | gawk '/cat|dog/{print $0}'
the cat is sleeping
這個應該很好理解,管道符號兩邊的任何正則表達式可以使用任意正則表達式模式(包括字符類)來定義文本
[root@wzp ~]# echo "the cat is sleeping" | gawk '/[ch]at|dog/{print $0}'
the cat is sleeping
[root@wzp ~]# echo "where is my hat?" | gawk '/[ch]at|dog/{print $0}'
where is my hat?
2.4.5、將表達式分組
正則表達式模式也可以使用圓括號分組,一個組合作爲 一個標準字符處理,如下看一個將分組和管道組合使用以創建可能的模式匹配組:
[root@wzp ~]# echo "gac" | gawk '/(c|d)a(t|g)/{print $0}'
[root@wzp ~]# echo "dag" | gawk '/(c|d)a(t|g)/{print $0}'
dag
[root@wzp ~]# echo "cat" | gawk '/(c|d)a(t|g)/{print $0}'
cat
實際上感覺這裏頭的括號作用跟中括號很接近。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章