1.1 什麼是正則表達式
1.1.1 定義
正則表達式是你所定義的模式模板(pattern template),Linux工具可以用它來過濾文本。Linux 工具(比如sed編輯器或gawk程序)能夠在處理數據時使用正則表達式對數據進行模式匹配。如 果數據匹配模式,它就會被接受並進一步處理;如果數據不匹配模式,它就會被濾掉
正則表達式模式利用通配符來描述數據流中的一個或多個字符。Linux中有很多場景都可以 使用通配符來描述不確定的數據
*.txt參數會讓ls命令只列出名字以txt結尾的文件。文件名中以txt之前可以有任意多個字符(包括什麼也沒有)。ls命令會讀取目錄中所有文件的信息,但只顯示跟通配符匹配的文件的信息。 正則表達式通配符模式的工作原理與之類似。正則表達式模式含有文本或特殊字符,爲sed 編輯器和gawk程序定義了一個匹配數據時採用的模板。可以在正則表達式中使用不同的特殊字符來定義特定的數據過濾模式
1.1.2 正則表達式的類型
使用正則表達式大的問題在於有不止一種類型的正則表達式。Linux中的不同應用程序可 能會用不同類型的正則表達式。這其中包括編程語言(Java、Perl和Python)、Linux實用工具(比 如sed編輯器、gawk程序和grep工具)以及主流應用(比如MySQL和PostgreSQL數據庫服務器)。
正則表達式是通過正則表達式引擎(regular expression engine)實現的。正則表達式引擎是 一套底層軟件,負責解釋正則表達式模式並使用這些模式進行文本匹配。 在Linux中,有兩種流行的正則表達式引擎: POSIX基礎正則表達式(basic regular expression,BRE)引擎
POSIX擴展正則表達式(extended regular expression,ERE)引擎
大多數Linux工具都至少符合POSIX BRE引擎規範,能夠識別該規範定義的所有模式符號。 遺憾的是,有些工具(比如sed編輯器)只符合了BRE引擎規範的子集。這是出於速度方面的考 慮導致的,因爲sed編輯器希望能儘可能快地處理數據流中的文本。
POSIX BRE引擎通常出現在依賴正則表達式進行文本過濾的編程語言中。它爲常見模式提供 了高級模式符號和特殊符號,比如匹配數字、單詞以及按字母排序的字符。gawk程序用ERE引擎 來處理它的正則表達式模式
1.2 定義BRE模式
基本的BRE模式是匹配數據流中的文本字符
20.2.1 純文本
第一個模式定義了一個單詞test。sed編輯器和gawk程序腳本用它們各自的print命令打印出 匹配該正則表達式模式的所有行。由於echo語句在文本字符串中包含了單詞test,數據流文本能 夠匹配所定義的正則表達式模式,因此sed編輯器顯示了該行。
第二個模式也定義了一個單詞,這次是trial。因爲echo語句文本字符串沒包含該單詞,所以 正則表達式模式沒有匹配,因此sed編輯器和gawk程序都沒打印該行。
你可能注意到了,正則表達式並不關心模式在數據流中的位置。它也不關心模式出現了多少 次。一旦正則表達式匹配了文本字符串中任意位置上的模式,它就會將該字符串傳回Linux工具。
關鍵在於將正則表達式模式匹配到數據流文本上。重要的是記住正則表達式對匹配的模式非 常挑剔。第一條原則就是:正則表達式模式都區分大小寫。這意味着它們只會匹配大小寫也相符的模式
第一次嘗試沒能匹配成功,因爲this在字符串中並不都是小寫,而第二次嘗試在模式中使 用大寫字母,所以能正常工作。
在正則表達式中,你不用寫出整個單詞。只要定義的文本出現在數據流中,正則表達式就能夠匹配
儘管數據流中的文本是books,但數據中含有正則表達式book,因此正則表達式模式跟數據 匹配。當然,反之正則表達式就不成立了
完整的正則表達式文本並未在數據流中出現,因此匹配失敗,sed編輯器不會顯示任何文本。
你也不用侷限於在正則表達式中只用單個文本單詞,可以在正則表達式中使用空格和數字
在正則表達式中,空格和其他的字符並沒有什麼區別
如果你在正則表達式中定義了空格,那麼它必須出現在數據流中。甚至可以創建匹配多個連續空格的正則表達式模式。
單詞間有兩個空格的行匹配正則表達式模式。這是用來查看文本文件中空格問題的好辦法
1.2.2 特殊字符
在正則表達式模式中使用文本字符時,有些事情值得注意。在正則表達式中定義文本字符時 有一些特例。有些字符在正則表達式中有特別的含義。如果要在文本模式中使用這些字符,結果會超出你的意料。
正則表達式識別的特殊字符包括:
. * [ ] ^ $ { } \ + ? | ( )
如果要用某個特殊字符作爲文本字符,就必須轉義。在轉義特殊字符時,你需要在它前面加 一個特殊字符來告訴正則表達式引擎應該將接下來的字符當作普通的文本字符。這個特殊字符就是反斜線(\)
如果要查找文本中的美元符,只要在它前面加個反斜線
由於反斜線是特殊字符,如果要在正則表達式模式中使用它,你必須對其轉義,這樣就產生 了兩個反斜線
( sed前面的兩個/ / 表示字段匹配,後面的\\表示轉義反斜線 )
儘管正斜線不是正則表達式的特殊字符,但如果它出現在sed編輯器或gawk程序的正則表達式中,你就會得到一個錯誤
要使用正斜線,也需要進行轉義
1.2.3 錨字符
默認情況下,當指定一個正則表達式模式時,只要模式出現在數據流中的任何地方,它就能匹配。有兩個特殊字符可以用來將模式鎖定在數據流中的行首或行尾
1. 鎖定在行首
脫字符(^)定義從數據流中文本行的行首開始的模式。如果模式出現在行首之外的位置, 正則表達式模式則無法匹配
要用脫字符,就必須將它放在正則表達式中指定的模式前面
脫字符會在每個由換行符決定的新數據行的行首檢查模式
只要模式出現在新行的行首,脫字符就能夠發現它。 如果你將脫字符放到模式開頭之外的其他位置,那麼它就跟普通字符一樣,不再是特殊字符了:
由於脫字符出現在正則表達式模式的尾部,sed編輯器會將它當作普通字符來匹配
--------------------------------------------------------------------------------------------------------------------------------------------
說明 如果指定正則表達式模式時只用了脫字符,就不需要用反斜線來轉義。但如果你在模式 中先指定了脫字符,隨後還有其他一些文本,那麼你必須在脫字符前用轉義字符--------------------------------------------------------------------------------------------------------------------------------------------
2. 鎖定在行尾
跟在行首查找模式相反的就是在行尾查找。特殊字符美元符($)定義了行尾錨點。將這個特殊字符放在文本模式之後來指明數據行必須以該文本模式結尾
使用結尾文本模式的問題在於你必須要留意到底要查找什麼
將行尾的單詞book改成複數形式,就意味着它不再匹配正則表達式模式了,儘管book仍然在數據流中。要想匹配,文本模式必須是行的後一部分。
3. 組合錨點
在一些常見情況下,可以在同一行中將行首錨點和行尾錨點組合在一起使用。在第一種情況 中,假定你要查找只含有特定文本模式的數據行。
sed編輯器忽略了那些不單單包含指定的文本的行。 第二種情況乍一看可能有些怪異,但極其有用。將兩個錨點直接組合在一起,之間不加任何文本,這樣過濾出數據流中的空白行。
考慮下面這個例子
定義的正則表達式模式會查找行首和行尾之間什麼都沒有的那些行。由於空白行在兩個換行 符之間沒有文本,剛好匹配了正則表達式模式。sed編輯器用刪除命令d來刪除匹配該正則表達式 模式的行,因此刪除了文本中的所有空白行。這是從文檔中刪除空白行的有效方法
1.2.4 點號字符
特殊字符點號用來匹配除換行符之外的任意單個字符。它必須匹配一個字符,如果在點號字符的位置沒有字符,那麼模式就不成立
你應該能夠明白爲什麼第一行無法匹配,而第二行和第三行就可以。第四行有點複雜。注意, 我們匹配了at,但在at前面並沒有任何字符來匹配點號字符。其實是有的!在正則表達式中, 空格也是字符,因此at前面的空格剛好匹配了該模式。第五行證明了這點,將at放在行首就不 會匹配該模式了
1.2.5 字符組 ( 字符組中字符不區分先後順序)
點號特殊字符在匹配某個字符位置上的任意字符時很有用。但如果你想要限定待匹配的具體字符在正則表達式中,這稱爲字符組(character class)。
可以定義用來匹配文本模式中某個位置的一組字符。如果字符組中的某個字符出現在了數據 流中,那它就匹配了該模式。
使用方括號來定義一個字符組。方括號中包含所有你希望出現在該字符組中的字符。然後你 可以在模式中使用整個組,就跟使用其他通配符一樣。這需要一點時間來適應,但一旦你適應了, 效果可是令人驚歎的
這裏用到的數據文件和點號特殊字符例子中的一樣,但得到的結果卻不一樣。這次我們成功 濾掉了只包含單詞at的行。匹配這個模式的單詞只有cat和hat。還要注意以at開頭的行也沒有匹配。字符組中必須有個字符來匹配相應的位置。 在不太確定某個字符的大小寫時,字符組會非常有用
可以在單個表達式中用多個字符組
正則表達式使用了3個字符組來涵蓋了3個字符位置含有大小寫的情況。
字符組不必只含有字母,也可以在其中使用數字
這個正則表達式模式匹配了任意含有數字0、1、2或3的行。含有其他數字以及不含有數字的行都會被忽略掉
可以將字符組組合在一起,以檢查數字是否具備正確的格式,比如電話號碼和郵編。但當你嘗試匹配某種特定格式時,必須小心。這裏有個匹配手機號碼出錯的例子
這個結果出乎意料。它成功過濾掉了不可能是手機號碼的那些過短的數字,因爲後一個字符組沒有字符可匹配。但它也通過了那個20位數,儘管我們只定義了11個字符組。 記住,正則表達式模式可見於數據流中文本的任何位置。經常有匹配模式的字符之外的其他字符。如果要確保只匹配11位數,就必須將匹配的字符和其他字符分開,要麼用空格,要麼像這 個例子中這樣,指明它們就在行首和行尾。
( 使用錨字符中的脫字符和美元符來實現 )
這個結果就好多了
字符組的一個極其常見的用法是解析拼錯的單詞,比如用戶表單輸入的數據。你可以創建正則表達式來接受數據中常見的拼寫錯誤
本例中的兩個sed打印命令利用正則表達式字符組來幫助找到文本中是否拼錯的單詞 memory和Zhifubao。
1.2.6 排除性字符組
在正則表達式模式中,也可以反轉字符組的作用。可以尋找組中沒有的字符,而不是去尋找組中含有的字符。要這麼做的話,只要在字符組的開頭加個脫字符
通過排除型字符組,正則表達式模式會匹配c或h之外的任何字符以及文本模式以及後邊的at。由於空格字符屬於這個範圍,它通過了模式匹配。但即使是排除,字符組仍然必須匹配一個字符,所以以at 開頭的行仍然未能匹配模式
1.2.7 區間
之前演示手機號碼的例子的時候,必須在每個字符組中列出所有可能的數字, 這實在有點麻煩。好在有一種便捷的方法可以讓人免受這番勞苦。可以用單破折線符號在字符組中表示字符區間。只需要指定區間的第一個字符、單破折線以及區間的後一個字符就行了。根 據Linux系統採用的字符集,正則表達式會包括此區間內的任意字符。 現在你可以通過指定數字區間來簡化手機號碼的例子。
這樣可是節省了不少的鍵盤輸入!每個字符組都會匹配0~9的任意數字。如果字母出現在數據中的任何位置,這個模式都將不成立
同樣的方法也適用於字母
新的模式[c-h]at匹配了首字母在字母c和字母h之間的單詞。這種情況下,只含有單詞at 的行將無法匹配該模式。
還可以在單個字符組指定多個不連續的區間
該字符組允許區間a~c、h~m中的字母出現在at文本前,但不允許出現d~g的字母
該模式不匹配fat文本,因爲它沒在指定的區間
1.2.8 特殊的字符組
除了定義自己的字符組外,BRE還包含了一些特殊的字符組,可用來匹配特定類型的字符。
可以在正則表達式模式中將特殊字符組像普通字符組一樣使用
使用特殊字符組可以很方便地定義區間。可以用[[:digit:]]來代替區間[0-9]
1.2.9 星號
在字符後面放置星號表明該字符必須在匹配模式的文本中出現0次或多次
這個模式符號廣泛用於處理有常見拼寫錯誤或在不同語言中有拼寫變化的單詞。舉個例子, 如果需要寫個可能用在美式或英式英語中的腳本,可以這麼寫:
模式中的u*表明字母u可能出現或不出現在匹配模式的文本中。類似地,如果你知道一個單 詞經常被拼錯,你可以用星號來允許這種錯誤
在可能出現的額外字母后面放個星號將允許接受拼錯的單詞。
另一個方便的特性是將點號特殊字符和星號特殊字符組合起來。這個組合能夠匹配任意數量的任意字符。它通常用在數據流中兩個可能相鄰或不相鄰的文本字符串之間
可以使用這個模式輕鬆查找可能出現在數據流中文本行內任意位置的多個單詞。 星號還能用在字符組上。它允許指定可能在文本中出現多次的字符組或字符區間
只要a和e字符以任何組合形式出現在b和t字符之間(就算完全不出現也行),模式就能夠匹配。如果出現了字符組之外的字符,該模式匹配就會不成立
1.3 擴展正則表達式
POSIX ERE模式包括了一些可供Linux應用和工具使用的額外符號。gawk程序能夠識別ERE 模式,但sed編輯器不能。
-------------------------------------------------------------------------------------------------------------------------------------------
警告 記住,sed編輯器和gawk程序的正則表達式引擎之間是有區別的。gawk程序可以使用大多 數擴展正則表達式模式符號,並且能提供一些額外過濾功能,而這些功能都是sed編輯器 所不具備的。但正因爲如此,gawk程序在處理數據流時通常才比較慢
-------------------------------------------------------------------------------------------------------------------------------------------
1.3.1 問號
問號類似於星號,不過有點細微的不同。問號表明前面的字符可以出現0次或1次,但只限於此。它不會匹配多次出現的字符
如果字符e並未在文本中出現,或者它只在文本中出現了1次,那麼模式會匹配。
與星號一樣,你可以將問號和字符組一起使用
如果字符組中的字符出現了0次或1次,模式匹配就成立。但如果兩個字符都出現了,或者其中一個字符出現了2次,模式匹配就不成立
1.3.2 加號
加號是類似於星號的另一個模式符號,但跟問號也有不同。加號表明前面的字符可以出現1次或多次,但必須至少出現1次。如果該字符沒有出現,那麼模式就不會匹配
如果字符e沒有出現,模式匹配就不成立。加號同樣適用於字符組,與星號和問號的使用方式相同
這次如果字符組中定義的任一字符出現了,文本就會匹配指定的模式
1.3.3 使用花括號
ERE中的花括號允許你爲可重複的正則表達式指定一個上限。這通常稱爲間隔(interval)。 可以用兩種格式來指定區間。
m:正則表達式準確出現m次。
m, n:正則表達式至少出現m次,至多n次。
這個特性可以精確調整字符或字符集在模式中具體出現的次數
---------------------------------------------------------------------------------------------------------------------------------------
警告 默認情況下,gawk程序不會識別正則表達式間隔。必須指定gawk程序的--re- interval 命令行選項才能識別正則表達式間隔。
---------------------------------------------------------------------------------------------------------------------------------------
這裏有個使用簡單的單值間隔的例子
通過指定間隔爲1,限定了該字符在匹配模式的字符串中出現的次數。如果該字符出現多次, 模式匹配就不成立。
很多時候,同時指定下限和上限也很方便。
在這個例子中,字符e可以出現1次或2次,這樣模式就能匹配;否則,模式無法匹配。
間隔模式匹配同樣適用於字符組
如果字母a或e在文本模式中只出現了1~2次,則正則表達式模式匹配;否則,模式匹配失敗
1.3.4 管道符號
管道符號允許你在檢查數據流時,用邏輯or方式指定正則表達式引擎要用的兩個或多個模 式。如果任何一個模式匹配了數據流文本,文本就通過測試。如果沒有模式匹配,則數據流文本 匹配失敗。
使用管道符號的格式如下:
expr1|expr2|...
這個例子會在數據流中查找正則表達式cat或dog。正則表達式和管道符號之間不能有空格, 否則它們也會被認爲是正則表達式模式的一部分。
管道符號兩側的正則表達式可以採用任何正則表達式模式(包括字符組)來定義文本。
1.3.5 表達式分組
正則表達式模式也可以用圓括號進行分組。當你將正則表達式模式分組時,該組會被視爲一 個標準字符。可以像對普通字符一樣給該組使用特殊字符。
舉個例子:
結尾的urday分組以及問號,使得模式能夠匹配完整的Saturday或縮寫Sat。
將分組和管道符號一起使用來創建可能的模式匹配組是很常見的做法
1.4 正則表達式實戰
1.4.1 目錄文件計數
先看一個shell腳本,它會對PATH環境變量中定義的目錄裏的可執行文件進行計數。要 這麼做的話,首先你得將PATH變量解析成單獨的目錄名.
根據Linux系統上應用程序所處的位置,PATH環境變量會有所不同。關鍵是要意識到PATH中 的每個路徑由冒號分隔。要獲取可在腳本中使用的目錄列表,就必須用空格來替換冒號。現在你會發現sed編輯器用一條簡單表達式就能完成替換工作
分離出目錄之後,你就可以使用標準for語句中來遍歷每個目錄
mypath=$(echo $PATH | sed 's/:/ /g')
for directory in $mypath
do
...
done
一旦獲得了單個目錄,就可以用ls命令來列出每個目錄中的文件,並用另一個for語句來遍 歷每個文件,爲文件計數器增值
實現目錄文件計數的腳本如下:
1.4.2 驗證電話號碼
前面的例子演示了在處理數據時,如何將簡單的正則表達式和sed配合使用來替換數據流中的字符。正則表達式通常用於驗證數據,確保腳本中數據格式的正確性
一個常見的數據驗證應用就是檢查電話號碼。數據輸入表單通常會要求填入電話號碼,而用 戶輸入格式錯誤的電話號碼是常有的事。在美國,電話號碼有幾種常見的形式:( 僅舉出例子加深知識點記憶 )
(123)456-7890
(123) 456-7890
123-456-7890
123.456.7890
這樣用戶在表單中輸入的電話號碼就有4種可能。正則表達式必須足夠強大,才能處理每一 種情況
在構建正則表達式時,最好從左手邊開始,然後構建用來匹配可能遇到的字符的模式。在這個例子中,電話號碼中可能有也可能沒有左圓括號。這可以用如下模式來匹配:
^\(?
脫字符用來表明數據的開始。由於左圓括號是個特殊字符,因此必須將它轉義成普通字符。 問號表明左圓括號可能出現,也可能不出現
緊接着就是3位區號。在美國,區號以數字2開始(沒有以數字0或1開始的區號),大可到9。 要匹配區號,可以用如下模式
[2-9][0-9]{2}
----------------------------------------------------------------------------------------------------------------------------------
這裏[2-9]是說第一位數字範圍在2-9之間,[0-9]是說範圍在0-9之間,緊跟着的{2}是說[0-9]的數字出現兩次,也就是2位0-9的數字。。只有當([2-9][0-9]{2})用小括號(),在一個分組下的時候才說明產生的{2} 與前面的[2-9]有關係
----------------------------------------------------------------------------------------------------------------------------------
這要求第一個字符是2~9的數字,後跟任意兩位數字。在區號後面,收尾的右圓括號可能存 在,也可能不存在
\)?
在區號後,存在如下可能:有一個空格,沒有空格,有一條單破折線或一個點。你可以對它 們使用管道符號,並用圓括號進行分組
(| |-|\.)
第一個管道符號緊跟在左圓括號後,用來匹配沒有空格的情形。你必須將點字符轉義,否則 它會被解釋成可匹配任意字符
緊接着是3位電話交換機號碼。這裏沒什麼需要特別注意的
[0-9]{3}
在電話交換機號碼之後,你必須匹配一個空格、一條單破折線或一個點(這次不用考慮匹配 沒有空格的情況,因爲在電話交換機號碼和其餘號碼間必須有至少一個空格)
( |-|\.)
最後,必須在字符串尾部匹配4位本地電話分機號
[0-9]{4}$
完整的模式如下
^\(?[2-9][0-9]{2}\)?(| |-|\.)[0-9]{3}( |-|\.)[0-9]{4}$
你可以在gawk程序中用這個正則表達式模式來過濾掉不符合格式的電話號碼。現在你只需要 在gawk程序中創建一個使用該正則表達式的簡單腳本,然後用這個腳本來過濾你的電話薄。記住, 在gawk程序中使用正則表達式間隔時,必須使用--re-interval命令行選項,否則就沒法得到正確的結果
腳本如下:
雖然從上面的清單中看不出來,但是shell腳本中的gawk命令是單獨在一行上的。可以將電話號碼重定向到腳本來處理
或者也可以將含有電話號碼的整個文件重定向到腳本來過濾掉無效的號碼
只有匹配該正則表達式模式的有效電話號碼纔會出現
1.4.3 解析郵件地址
如今這個時代,電子郵件地址已經成爲一種重要的通信方式。驗證郵件地址成爲腳本程序員 的一個不小的挑戰,因爲郵件地址的形式實在是千奇百怪。郵件地址的基本格式爲:
username@hostnameusername值可用字母數字字符以及以下特殊字符:
點號
單破折線
加號
下劃線
在有效的郵件用戶名中,這些字符可能以任意組合形式出現。郵件地址的hostname部分由 一個或多個域名和一個服務器名組成。服務器名和域名也必須遵照嚴格的命名規則,只允許字母 數字字符以及以下特殊字符:
點號
下劃線
服務器名和域名都用點分隔,先指定服務器名,緊接着指定子域名,後是後面不帶點號的頂級域名。
頂級域名的數量在過去十分有限,正則表達式模式編寫者會嘗試將它們都加到驗證模式中。 然而遺憾的是,隨着互聯網的發展,可用的頂級域名也增多了。這種方法已經不再可行。
從左側開始構建這個正則表達式模式。我們知道,用戶名中可以有多個有效字符。這個相當容易
^([a-zA-Z0-9_\-\.\+]+)@
這個分組指定了用戶名中允許的字符,加號表明必須有至少一個字符。下一個字符很明顯是 @,沒什麼意外的。
hostname模式使用同樣的方法來匹配服務器名和子域名
([a-zA-Z0-9_\-\.]+)
這個模式可以匹配文本
server
server.subdomain
server.subdomain.subdomain
對於頂級域名,有一些特殊的規則。頂級域名只能是字母字符,必須不少於二個字符(國家 或地區代碼中使用),並且長度上不得超過五個字符。下面就是頂級域名用的正則表達式模式
\.([a-zA-Z]{2,5})$
將整個模式放在一起會生成如下模式。:
^([a-zA-Z0-9_\-\.\+]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$
這個模式會從數據列表中過濾掉那些格式不正確的郵件地址。現在可以創建腳本來實現這個 正則表達式了
小結:
如果你在shell腳本中處理數據文件,就必須熟悉正則表達式。正則表達式在Linux實用工具、 編程語言以及採用了正則表達式引擎的應用程序中均有實現。在Linux中有一些不同的正則表達 式引擎。流行的兩種是POSIX基礎正則表達式(BRE)引擎和POSIX擴展正則表達式(ERE) 引擎。sed編輯器基本符合BRE引擎,而gawk程序則使用了ERE引擎中的大多數特性。
正則表達式定義了用來過濾數據流中文本的模式模板。模式由標準文本字符和特殊字符的組 成。正則表達式引擎用特殊字符來匹配一系列單個或多個字符,這類似於其他應用程序中通配符 的工作方式。
通過結合字符和特殊字符,你能夠定義出匹配大多數數據類型的模式。然後你可以用sed編 輯器或gawk程序從大型數據流中過濾特定數據,或者驗證從其他數據輸入應用程序收到的數據。
下一章將會更深入地使用sed編輯器來進行高級文本處理。sed編輯器中的許多高級功能讓它在處理大型數據流和過濾數據時非常有用