Linux
中大部分時(shí)間都是在處理文本內(nèi)容,而為了更加快速和自動(dòng)化的處理文本,我們就需要正則表達(dá)式。正則表達(dá)式提供了功能強(qiáng)大跛十、靈活而又高效的方式來(lái)處理文本。正則表達(dá)式的模式匹配可以使你在大量的文本內(nèi)容中快速找到特定的字符序列秕硝。驗(yàn)證文本來(lái)確保所匹配的模式符合某種規(guī)則:如電子郵件地址芥映,還可以提起,編輯远豺,刪除文本字符串奈偏。對(duì)于快速處理大量的文本內(nèi)容,正則表達(dá)式是不可或缺的工具躯护。
正則表達(dá)式是你所定義的模式模版(pattern template
)惊来,Linux
工具可以使用它來(lái)過(guò)濾和處理文本。如grep
,sed,
awk`等工具都支持正則表達(dá)式棺滞。如果數(shù)據(jù)匹配模式唁盏,它就會(huì)被接受并進(jìn)行下一步處理内狸,如果數(shù)據(jù)不匹配模式則它會(huì)被丟棄。下圖描述下這個(gè)過(guò)程:
文本數(shù)據(jù) -----> 正則表達(dá)式 -------> 匹配的數(shù)據(jù)
|
|
|
↓
丟棄的數(shù)據(jù)
1. 正則表達(dá)式的類(lèi)型
正則表達(dá)式是通過(guò)正則表達(dá)式引擎(regular expression engine)實(shí)現(xiàn)的厘擂。正則表達(dá)式引擎是一套底層軟件,負(fù)責(zé)解釋正則表達(dá)式模式并使用這些模式進(jìn)行文本匹配锰瘸。
在Linux
中刽严,有兩種流行的正則表達(dá)式引擎:
- BRE: POSIX基礎(chǔ)正則表達(dá)式引擎(basic regular expression)
- ERE: POSIX擴(kuò)展正則表達(dá)式引擎(extended regular expression)
2. BRE模式
最基本的BRE
模式就是匹配數(shù)據(jù)流中的文本字符。
2.1 純文本
下面使用sed
程序使用正則表達(dá)式過(guò)濾數(shù)據(jù).
$ echo "This is a test" | sed -n '/test/p'
This is a test
$ echo "This is a test" | sed -n '/tset/p'
$
$ echo "This is a test" | sed -n '/this/p'
$
$ echo "This is a test" | sed -n '/This/p'
This is a test
$
第一個(gè)例子使用test
來(lái)匹配數(shù)據(jù)流中的文本避凝。由于echo
語(yǔ)句中包含了單詞test
,所以sed
能匹配到該文本模式舞萄,然后使用p
命令輸出匹配到的文本行。第二個(gè)例子使用tset
管削,然后文本流中并沒(méi)有這個(gè)單詞倒脓,所以sed
并沒(méi)有匹配到這個(gè)模式,就沒(méi)有輸出任何數(shù)據(jù)含思。第三個(gè)例子使用的this
崎弃,這次也沒(méi)有匹配成功,應(yīng)該文本流中并沒(méi)有小寫(xiě)的this
,所以也沒(méi)有匹配成功含潘。第四次文本流中出現(xiàn)了This
饲做,所以匹配成功。
正則表達(dá)式并不關(guān)心模式匹配出現(xiàn)在數(shù)據(jù)流的位置遏弱,它也不關(guān)心模式出現(xiàn)了多少次盆均,一旦匹配到該模式,則會(huì)把數(shù)據(jù)流中的文本行傳回給Linux
命令漱逸。但是正則表達(dá)式會(huì)區(qū)分大小寫(xiě)泪姨,它們只匹配字符序列和模式一樣的文本行。匹配到的文本行中必須出現(xiàn)模式饰抒,而且文本流中匹配到的字符序列必須和模式中的字符順序一樣肮砾。
$ echo "There are many dog." | sed -n '/dogs/p'
$
$ echo "There are many dogs." | sed -n '/dogs/p'
There are many dogs.
$
上面的第一個(gè)例子匹配dogs
,但是文本流中只有dog
,所以匹配失敗。第二個(gè)例子則成功的匹配到了dogs
循集。所以模式中的字符串必須安裝順序全部出現(xiàn)在文本流中才能匹配成功唇敞。
如果只使用上面這張字符串匹配則并不能一次處理大量的文本。如果想一次匹配很多字符串則必須把它們都寫(xiě)出來(lái)咒彤,這并不能發(fā)揮正則表達(dá)式的強(qiáng)大能力疆柔,下面會(huì)介紹正則表達(dá)式中非常有用的一些特殊字符來(lái)構(gòu)造復(fù)雜的模式。
2.2 特殊字符
有些字符在正則表達(dá)式中有特別的含義镶柱,結(jié)合這些特殊字符可以構(gòu)造復(fù)雜的模式匹配旷档。這些特殊字符包括:
.*[]^$\+?|()
因?yàn)檫@些特殊字符在正則表達(dá)式中具有特殊意義,如果只是想把它們作為文本字符來(lái)匹配則需要使用\
來(lái)轉(zhuǎn)義歇拆。而且如果想匹配反斜線(xiàn)也必須使用\
來(lái)轉(zhuǎn)義鞋屈。
$ echo "\ is a special character" | sed -n '///p'
$
$ echo "\ is a special character" | sed -n '/\//p'
\ is a special character
$
2.3 錨字符
默認(rèn)情況下范咨,當(dāng)匹配模式時(shí),只要模式在數(shù)據(jù)流的任何位置出現(xiàn)厂庇,模式匹配都會(huì)成功渠啊。有兩個(gè)字符可以現(xiàn)在模式在數(shù)據(jù)流中出現(xiàn)的位置。
2.3.1 行首
^
字符定義從數(shù)據(jù)流中文本行的行首開(kāi)始的模式权旷。如果模式出現(xiàn)在行首則匹配成功替蛉,否則無(wú)法匹配。要使用^
拄氯,必須將它放到正則表達(dá)式中模式的前面躲查。
$ echo "Make American great again." | sed -n '/^make/p'
$
$ echo "Make American great again." | sed -n '/^Make/p'
Make American great again.
2.3.2 行尾
和^
一樣,$
表示模式必須出現(xiàn)在行尾译柏。將這個(gè)特殊字符放在文本模式后面來(lái)指明數(shù)據(jù)必須以該文本模式結(jié)尾镣煮。
$ echo "MAKE AMERICAN GREAT AGAIN." | sed -n '/again$/p'
$
$ echo "MAKE AMERICAN GREAT AGAIN." | sed -n '/AGAIN$/p'
MAKE AMERICAN GREAT AGAIN.
2.3.2 組合錨點(diǎn)
在某些情況下可以在同一行中把行首錨點(diǎn)和行尾錨點(diǎn)組合起來(lái)使用。一種情況是為了查找只含有特定文本的數(shù)據(jù)行鄙麦。
$ cat data1.txt
make american great again.
Make american great again.
MAKE AMERICAN GREAT AGAIN.
$ sed -n '/^MAKE AMERICAN GREAT AGAIN.$/p' data1.txt
MAKE AMERICAN GREAT AGAIN.
$
還有一個(gè)情況就是在行首錨點(diǎn)和行尾錨點(diǎn)之間不加任何文本典唇,這樣就會(huì)過(guò)濾出數(shù)據(jù)流中的空白行。
$ cat data2.txt
make american great again.
Make american great again.
MAKE AMERICAN GREAT AGAIN.
$ sed -n '/^$/d' data2.txt
make american great again.
Make american great again.
MAKE AMERICAN GREAT AGAIN.
$
上面定義的正則表達(dá)式模式會(huì)查找行首和行尾之間什么都沒(méi)有的數(shù)據(jù)行黔衡。然后使用d
命令來(lái)刪除匹配到的空白行蚓聘,因此輸出的文本流中就沒(méi)有空白行了。這是從文檔中刪除空白行的很有效的方法盟劫。
2.4 點(diǎn)字符
特殊字符點(diǎn)號(hào)用來(lái)匹配除換行符之外的任意一個(gè)字符夜牡。在點(diǎn)號(hào)出現(xiàn)的位置必須有且只有一個(gè)字符,才能匹配成功
$ cat zip.txt
bzip2
bunzip
gzip
gunzip
unzip
zip
$ sed -n '/.zip/p' zip.txt
bzip2
bunzip
gzip
gunzip
unzip
$
上面的例子中.zip
表示最少得匹配4個(gè)字符侣签,其中第一個(gè)可以是任意字符塘装,后面三個(gè)必須是zip
。所以最后一行沒(méi)有匹配成功影所,其他則都匹配成功蹦肴。
2.5 字符組
特殊字符點(diǎn)號(hào)在匹配某個(gè)位置上的任意字符時(shí)很有效,但是如果想限制字符的范圍的時(shí)候就沒(méi)有那么給力了猴娩。但是正則表達(dá)式中的字符組(character class)
可以用來(lái)限定待匹配的字符是一組字符中任意一個(gè)阴幌。如果字符組中的某個(gè)字符出現(xiàn)在數(shù)據(jù)流中則匹配成功。
使用方括號(hào)來(lái)定義一個(gè)字符組卷中。方括號(hào)中的字符是你希望匹配的字符矛双,然后你就可以使用整個(gè)組來(lái)匹配字符。
$ sed -n '[bg]zip' zip.txt
bzip2
gzip
$
上面的模式匹配中的方括號(hào)就是字符組蟆豫,只要匹配到其中的任何一個(gè)字符都會(huì)匹配成功议忽。這個(gè)匹配模式可以匹配bzip
和gzip
。字符組中的位置必須要有其中的一個(gè)字符出現(xiàn)才能匹配成功十减。字符組中不僅能使用字符栈幸,也可以使用數(shù)字愤估。
$ cat data3.txt
This is test line0.
This is test line1.
This is test line2.
This is test line3.
This is a end line.
$ sed -n '/[12345]/p' data3.txt
This is test line1.
This is test line2.
This is test line3.
$
這個(gè)正則表達(dá)式模式匹配數(shù)據(jù)行中有數(shù)字0
,1
,2
,3
,4
,5
的行。其他數(shù)字以及沒(méi)有數(shù)字的行都會(huì)被過(guò)濾速址。
可以講字符組組合在一起以檢查特定的字符序列是否符合某種格式:比如電話(huà)號(hào)碼和郵編玩焰。
cat data4.txt
606333
462018
2230011
4353
51900
$ sed -n '
> /[0123456789][0123456789][0123456789][0123456789][0123456789][0123456789]/
> ' data4.txt
606333
462018
2230011
上面的例子成功的過(guò)濾了6
位數(shù)以下的郵編,但是它也讓7
位數(shù)的郵編通過(guò)了模式匹配壳繁。正則表達(dá)式只有在文本流中匹配到了最小的模式字符串就匹配成功震捣,上面的例子中7
位數(shù)字中的前6
位已經(jīng)匹配成功,則整行文本都會(huì)匹配成功闹炉。如果想只匹配長(zhǎng)度為6個(gè)數(shù)字的郵編則可以使用行首行尾限定符:
$ sed -n '
> /^[0123456789][0123456789][0123456789][0123456789][0123456789][0123456789]$/
> ' data4.txt
606333
462018
$
2.6 區(qū)間
上面的例子只是匹配6個(gè)數(shù)字的郵編,如果我們想匹配11位的移動(dòng)手機(jī)號(hào)碼润樱,如果按照上面的方式...:( 渣触。好在有一種可以方式可以避免寫(xiě)那么多字母和數(shù)字。這就是區(qū)間壹若,只要在方括號(hào)的開(kāi)始和結(jié)束指定一個(gè)字符嗅钻,中間使用破折號(hào)分隔這兩個(gè)字符就可以了。現(xiàn)在可以?xún)?yōu)化下上面的郵編的例子:
$ sed -n '
> /^[0-9][0-9][0-9][0-9][0-9][0-9]$/
> ' data4.txt
606333
462018
$
這個(gè)模式和上面的是一樣的店展,只能匹配6位數(shù)字养篓。同樣的方法也使用于字母字符:
$ sed -n '/[c-h]at/p' data5.txt
The cat is sleeping.
That is a very nice hat.
$
上面的例子中[c-h]
匹配c
和h
之間的所有字母。還可以在單個(gè)字符組中指定多個(gè)字符區(qū)間:
$ sed -n '/[c-hx-z]at/p' data5.txt
The cat is sleeping.
That is a very nice hat.
$
2.7 排除型字符組
在正則表達(dá)式中我們也可以反轉(zhuǎn)字符組的作用赂蕴。就是匹配不在字符組中的字符柳弄。在字符組的前面加上^
,就可以匹配字符組之外的字符了概说。
$ sed -n '/[^c-h]at/p' data5.txt
$
通過(guò)排除型字符組碧注,可以匹配除了c
和h
之間的所有字符。
2.8 特殊的字符組
除了自己定義的字符組之外糖赔,BRE
還包含了一些特殊的字符組萍丐,可以用來(lái)匹配特定類(lèi)型的字符。
組 | 描述 |
---|---|
[[:alpha:]] | 匹配任意字母字符放典,不管是大寫(xiě)還是小寫(xiě) |
[[:alnum:]] | 匹配任意字母數(shù)字字符09逝变、AZ或a~z |
[[:blank:]] | 匹配空格或制表符 |
[[:digit:]] | 匹配0~9之間的數(shù)字 |
[[:lower:]] | 匹配小寫(xiě)字母字符a~z |
[[:print:]] | 匹配任意可打印字符 |
[[:punct:]] | 匹配標(biāo)點(diǎn)符號(hào) |
[[:space:]] | 匹配任意空白字符:空格、制表符奋构、NL壳影、FF、VT和CR |
[[:upper:]] | 匹配任意大寫(xiě)字母字符A~Z |
可以在正則表達(dá)式中將特殊表達(dá)式當(dāng)作普通字符組使用:
$ echo "abc" | sed -n '/[[:digit:]]/p'
$
$ echo "abc" | sed -n '/[[:alpha:]]/p'
abc
$ echo "abc123" | sed -n '/[[:digit:]]/p'
abc123
$ echo "This is, a test" | sed -n '/[[:punct:]]/p'
This is, a test
$ echo "This is a test" | sed -n '/[[:punct:]]/p'
$
2.9 星號(hào)
在字符后面放置星號(hào)表面前面的字符必須在模式匹配中出現(xiàn)0次或多次声怔。
$ echo "k" | sed -n '/o*k/p'
k
$ echo "ok" | sed -n '/o*k/p'
ok
$ echo "oooook" | sed -n '/o*k/p'
oooook
$
o*
表示模式中可以有0個(gè)o
,1個(gè)o
,或者多個(gè)o
态贤。
3. 擴(kuò)展正則表達(dá)式
POSIX ERE
模式包括了BRE
而且還提供了一些額外的特殊符號(hào)。下面來(lái)介紹ERE
模式中剩下的特殊符號(hào)醋火。
3.1 問(wèn)號(hào)
問(wèn)號(hào)類(lèi)似于星號(hào)悠汽,但是問(wèn)號(hào)和星號(hào)又不同箱吕。問(wèn)號(hào)表示前面的字符只能出現(xiàn)0
次或1
次,不能是其他次數(shù)柿冲。就是說(shuō)問(wèn)號(hào)前面的字符要么不出現(xiàn)茬高,要么只出現(xiàn)一次,其他情況都會(huì)匹配失敗假抄。
$ echo "ct" | gawk '/ca?t/{print $0}'
ct
$ echo "cat" | gawk '/ca?t/{print $0}'
cat
$ echo "caat" | gawk '/ca?t/{print $0}'
$
$ echo "caaat" | gawk '/ca?t/{print $0}'
$
?
表示前面的字符只能出現(xiàn)0
次或1
次怎栽,其他情況都不會(huì)匹配成功。?
也可以和字符組一起使用:
$ echo "ct" | gawk '/c[au]?t/{print $0}'
ct
$ echo "cat" | gawk '/c[au]?t/{print $0}'
cat
$ echo "cut" | gawk '/c[au]?t/{print $0}'
cut
$ echo "cot" | gawk '/c[au]?t/{print $0}'
$
$ echo "caut" | gawk '/c[au]?t/{print $0}'
$
$ echo "caet" | gawk '/c[au]?t/{print $0}'
$
3.2 加號(hào)
+
和*
宿饱、?
類(lèi)似熏瞄,加號(hào)表示前面的字符最少出現(xiàn)1
次。如果加號(hào)前面的字符沒(méi)有出現(xiàn)則匹配失敗谬以。
$ echo "ct" | gawk '/ca+t/{print $0}'
$
$ echo "cat" | gawk '/ca+t/{print $0}'
cat
$ echo "cut" | gawk '/ca+t/{print $0}'
$
$ echo "caat" | gawk '/ca+t/{print $0}'
caat
$ echo "caet" | gawk '/ca+t/{print $0}'
$
$ echo "caaat" | gawk '/ca+t/{print $0}'
caaat
$
如果a
字符沒(méi)有出現(xiàn)則匹配失敗强饮。+
也可以作用于字符組:
$ echo "ct" | gawk '/c[au]+t/{print $0}'
$
$ echo "cat" | gawk '/c[au]+t/{print $0}'
cat
$ echo "cut" | gawk '/c[au]+t/{print $0}'
cut
$ echo "caat" | gawk '/c[au]+t/{print $0}'
caat
$ echo "caut" | gawk '/c[au]+t/{print $0}'
caut
$ echo "caaaaut" | gawk '/c[au]+t/{print $0}'
caaaaut
$
3.3 花括號(hào)
ERE
中的花括號(hào)允許前面的模式可以重復(fù)出現(xiàn)的次數(shù)。這通過(guò)稱(chēng)為間隔(interval
)为黎∮史幔可以用四種方式來(lái)指定:
限定符 | 描述 |
---|---|
{n} | 匹配前面的元素,如果它確切地出現(xiàn)了 n 次铭乾。 |
{n,m} | 匹配前面的元素剪廉,如果它至少出現(xiàn)了 n 次,但是不多于 m 次炕檩。 |
{n,} | 匹配前面的元素斗蒋,如果它出現(xiàn)了 n 次或多于 n 次。 |
{,m} | 匹配前面的元素捧书,如果它出現(xiàn)的次數(shù)不多于 m 次吹泡。 |
$ echo "ct" | grep -E 'ca{1}t'
$
$ echo "cat" | grep -E 'ca{1}t'
cat
$ echo "caat" | grep -E 'ca{1}t'
$
設(shè)置間隔為1
則表示a
必須且只能出現(xiàn)一次,其他情況都不會(huì)匹配成功经瓷。grep -E
可以識(shí)別ERE
模式下的正則表達(dá)式爆哑。也可以單獨(dú)設(shè)置重復(fù)出現(xiàn)的最少次數(shù)和最大次數(shù),也可以設(shè)置一起設(shè)置最小和最大次數(shù)舆吮。
3.4 Alternation(交替)
管道符號(hào)允許你在檢查正則表達(dá)式時(shí)揭朝,用邏輯OR
方式指定多個(gè)模式。如果匹配了其中任何一個(gè)單獨(dú)的模式則整個(gè)模式匹配成功色冀,如果沒(méi)有模式匹配上潭袱,則數(shù)據(jù)流匹配失敗宰睡。使用管道符號(hào)的格式如下:
expr1 | expr2 | ...
$ echo "There is a dog." | gawk '/cat|dog/{print $0}'
There is a dog.
$ echo "There is a cat." | gawk '/cat|dog/{print $0}'
There is a cat.
$ echo "He has a cat" | gawk '/[ch]at|dog/{print $0}'
He has a cat
$
管道符號(hào)兩側(cè)的正則表達(dá)式可以采用任何形式的正則表達(dá)式來(lái)定義文本(包括字符組)酿愧。
3.5 表達(dá)式分組
可以使用圓括號(hào)對(duì)正則表達(dá)式分組,當(dāng)你將正則表達(dá)式分組時(shí)挽牢,該組會(huì)被視為一個(gè)標(biāo)準(zhǔn)字符⊥冢可以像普通字符一樣給改組使用特殊字符嘉抓。
$ echo "Sat" | gawk '/Sat(urday)?/{print $0}'
Sat
$ echo "Saturday" | gawk '/Sat(urday)?/{print $0}'
Saturday
$
結(jié)尾的urday
分組就像一個(gè)普通字符,后面的問(wèn)號(hào)表示urday
可以不出現(xiàn)或者只出現(xiàn)一次晕窑。將分組和管道一起使用是很常見(jiàn)的做法抑片。
$ echo "cat" | gawk '/(c|b)a(b|t)/{print $0}'
cat
$ echo "cab" | gawk '/(c|b)a(b|t)/{print $0}'
cab
$ echo "bat" | gawk '/(c|b)a(b|t)/{print $0}'
bat
$ echo "bab" | gawk '/(c|b)a(b|t)/{print $0}'
bab
$ echo "tab" | gawk '/(c|b)a(b|t)/{print $0}'
$
$ echo "tac" | gawk '/(c|b)a(b|t)/{print $0}'
$
上面就是 POSIX
的正則表達(dá)式。每種編程語(yǔ)言都會(huì)在 POSIX
基礎(chǔ)上提供更加豐富的符號(hào)集杨赤。上面只是最基本的正則表達(dá)式敞斋,其他更加復(fù)雜的就需要你們自己去學(xué)習(xí)了。加油 :)