歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Unix知識 >> Unix基礎知識 >> 磨練構建正則表達式模式的技能-用於系統管理的正則表達式

磨練構建正則表達式模式的技能-用於系統管理的正則表達式

日期:2017/3/3 15:24:09   编辑:Unix基礎知識

簡介:通過本文的學習,您可以增加一些有用的設計實際正則表達式 (regexp) 的技能。構建正則表 達式是任何管理員日常工作中的一部分。為了構造返回所需條件的成功正則表達式,需要學習以模式匹配 的角度進行思考,而這種技能需要花大量的時間進行練習。

引言

UNIX® 管理員每天都需要構建和使用正則表達式 (regexp) 進行文本模式匹配。大多數語言都支 持正則表達式的某種實現。有的應用程序(如 EMACS)具有正則表達式搜索功能,並且您可以通過各種命 令行工具使用正則表達式。無論什麼應用程序,構建正確的正則表達式的關鍵之處在於,識別僅滿足需要 匹配的數據的模式,以便在輸入中排除其他不必要的內容。

出於這個目的,本文將逐步介紹幾種正則表達式模式構建技巧,並介紹它們如何幫助您完成各種常規 任務。

使用正則表達式 (regexp)

除非特別說明,否則本文中使用的示例都是擴展可移植操作系統接口(擴展 POSIX)的正則表達式。 如果通過命令行(如使用 egrep 實用工具)使用它們,您應該根據需要引用各種正則表達式。請記住, 不同的正則表達式實現之間存在一些區別,您可能不得不適應所使用的特定的工具、應用程序或語言中的 具體實現。

匹配整行內容

^ 元字符匹配行首,而 $ 匹配行尾,如果將它們組合在一起(如 ^$),它們將匹配空行。(這個表 達式的鏡像,即 $^,是不可能匹配成功的,它將永遠 都無法匹配到有效行。)這個基本的正則表達式是 許多復雜正則表達式的基礎,如果您還不習慣使用這個基本的正則表達式,那麼您應該逐步養成使用它的 習慣。使用它來構建匹配整行內容 的模式。

在用戶字典文件 (/usr/dict/words) 中搜索是一個很好的基本模式。(有些版本的 UNIX 將用戶字典 放在 /usr/share/dict/words 中。)

例如,假設您忘記了如何拼寫單詞 fuchsia。其中是否包含 sh 或 cs 呢?您所知道的只是,它以 fu 開頭並以 ia 結尾。

嘗試使用這個模式進行搜索:

$ egrep -i '^fu.*ia$' /usr/dict/words

-i 標志表示在搜索過程中不區分大小寫。在這個示例中,因為 fuchsia 拼寫正確,所以在返回的單 詞中包括這個單詞。

根據長度匹配行

使用大括號元字符 ({ }) 指定前面的正則表達式匹配多少次,如表 1 所示。當您將它們添加到剛才 介紹的整行搜索中時,您可以指定行的長度。

表 1. 大括號元字符的含義

示例描述

{X} 這個字符對前面的正則表達式匹配 X 次。 {X,} 這個字符對前面的正則表達式匹配 X 或更多 次。 {X,Y} 這個字符對前面的正則表達式匹配至少 X 而不超過 Y 次。

並不是所有擴展正則表達式的實現都支持大括號。此外,根據具體的實現,您可能需要先使用反斜槓 對其進行轉義。

您可以使用這個正則表達式得到字典中以單詞長度為順序的報告。所獲得結果的數目取決於本地系統 的字典文件中單詞的數目,然而,它應該與清單 1 所示類似。在這個示例中,最常見的單詞長度是 9 個 字母,該字典中有 32,380 個匹配單詞。該字典中不包括 25 個字母或更長的單詞,並且最長的單詞並不 是您認為的 21 個字母長的 disestablishmentarian(有 81 個同樣長度的單詞,包括 superincomprehensible 和 phoneticohieroglyphic),這個 UNIX 字典中最長的單詞有 5 個,包括 pathologicopsychological。

清單 1. 計算字典中 X 個字母的單詞的個數

$ for i in `seq 1 32`
> {
>  echo "There are" `egrep '^.{'$i'}$' /usr/dict/words \
  | wc -l` "$i-letter words in the dictionary."
> }
There are 52 1-letter words in the dictionary.
There are 155 2-letter words in the dictionary.
There are 1351 3-letter words in the dictionary.
There are 5110 4-letter words in the dictionary.
There are 9987 5-letter words in the dictionary.
There are 17477 6-letter words in the dictionary.
There are 23734 7-letter words in the dictionary.
There are 29926 8-letter words in the dictionary.
There are 32380 9-letter words in the dictionary.
There are 30867 10-letter words in the dictionary.
There are 26011 11-letter words in the dictionary.
There are 20460 12-letter words in the dictionary.
There are 14938 13-letter words in the dictionary.
There are 9762 14-letter words in the dictionary.
There are 5924 15-letter words in the dictionary.
There are 3377 16-letter words in the dictionary.
There are 1813 17-letter words in the dictionary.
There are 842 18-letter words in the dictionary.
There are 428 19-letter words in the dictionary.
There are 198 20-letter words in the dictionary.
There are 82 21-letter words in the dictionary.
There are 41 22-letter words in the dictionary.
There are 17 23-letter words in the dictionary.
There are 5 24-letter words in the dictionary.
There are 0 25-letter words in the dictionary.
There are 0 26-letter words in the dictionary.
There are 0 27-letter words in the dictionary.
There are 0 28-letter words in the dictionary.
There are 0 29-letter words in the dictionary.
There are 0 30-letter words in the dictionary.
There are 0 31-letter words in the dictionary.
There are 0 32-letter words in the dictionary.
$

匹配單詞

圍繞字符 \< 和 \> 是非常有用的模式構造器:它們將要匹配的整個單詞 括起來,這表示,它 們不會匹配帶括號的模式,除非該模式本身就是一個單詞。單詞 定義為兩側由非單詞字符描述的、任意 數目組成單詞的字符(數字、字母和下劃線字符)。非單詞字符包括下面的所有字符:

行首

空白字符

標點符號

行尾

任何除字母、數字或下劃線以外的字符

這些圍繞字符可以節省大量的時間,但是它們常常沒有被充分地利用,可能是因為並非所有的正則表 達式實現都支持它們。如果您的正則表達式實現支持它們,那麼您應該逐步養成使用它們的習慣。

將需要單獨匹配的單詞括起來,如下所示:

\<system\>

這個示例中的正則表達式不會匹配單詞 ecosystem、systemic 或 system/70,也不會匹配模式 system 出現在行中任意位置的那些行,它將僅僅 匹配 system 作為獨立的單詞出現的那些行。

圍繞字符與圓括號中的分組結合在一起,可以用來匹配部分 單詞。

要匹配包含以 pre 開頭 的單詞的那些行,可以使用:

\<\(pre\).*\>

前面的示例將匹配包含單詞 preface 和 preposterous 的行,但不會匹配 spread 或 Dupre。

匹配重復單詞

這裡介紹一種使用單詞圍繞字符匹配重復單詞的快速方法,重復單詞表示一個單詞在空格之後再次出 現。您還可以使用逆向引用,這是大多數流行的正則表達式實現中的一種遞歸特性,它可以匹配模式本身 的某一部分。(將模式中需要引用的部分使用圓括號括起來,然後使用反斜槓加上需要進行引用的圍繞字 符編號來調用逆向引用:1 表示第一個圓括號分組,2 表示第二個圓括號分組,依此類推。)

要查找重復的單詞,搜索在任意數目的空格之後再次出現該單詞的情況,可以通過對第一個使用圓括 號的部分進行逆向引用來實現:

(\<.*\>)( )+\1

這個示例匹配縮寫形式和任何類型的單詞,但是它不會匹配由標點符號分隔的重復單詞,如 It's been a long, long time。

要匹配所有的重復單詞,包括由空格和 任意標點符號分隔的重復單詞,可以使用下面的表達式:

(\<.*\>).?( )+\1

如果需要對這些正則表達式使用 grep,則務必使用 -i 標志,以便在搜索中不區分大小寫。

匹配小時

讓我們再來看另外一類常見的問題:時間和日期。這裡介紹了一些設計匹配正確模式的正則表達式所 需要考慮的事項。

您無法搜索任何兩位的數字來匹配分鐘和秒,因為它們僅僅是從 0 到 59,要匹配它們,您需要使用 方括號將表示十位和個位的范圍括起來:

要匹配標准的 12 或 24 小時格式的小時,可以使用下面的表達式:

(([0-1]?[0-9])|([2][0-3])):([0-5][0-9])(:[0-5][0-9])?

要匹配 12 小時 AM/PM 格式、帶或不帶秒數的時間,甚至匹配大寫或小寫、不帶後綴 AM 或 PM 標識 符的時間,可以使用下面的表達式:

([^0-9])([0-1]?[0-9]){1}(((:([0-5]){1}([0-9]){1}){1,2})|(( )?([AP]M)|([ap] m)))?

如果在上一個示例中沒有開始的否定語句,它將匹配不帶冒號的時間,這將取決於輸入數據,可能會 匹配中波廣播電台(在美國稱為調幅 AM 電台),如 1450 AM。

匹配月份

匹配 12 個月中的任何月份需要一個使用 | 操作符進行分隔的列表,但有時會使用不同的方式對日期 進行縮寫:

要查找完整拼寫或三字母縮寫的 12 個月份,可以使用下面的表達式(位於一行):

Jan(uary)?|Feb(uary)?|Mar(ch)?|Apr(il)?|May|Jun(e)?|Jul(y)?|
Aug(ust)?|Sep(tember)?|Oct(ober)?|Nov(ember)?|Dec(ember)?

您可以加以想象並搜索完整拼寫或三字母縮寫的變形,即僅當後面緊跟著一個空格或點號的情況,可 以使用下面的表達式(位於一行):

Jan(uary| |\.)|Feb(uary| |\.)|Mar(ch| |\.)|Apr(il| |\.)|May( |\.)|Jun(e| |\.) |Jul(y| |\
.)|Aug(ust| |\.)|Sep(tember| |\.)|Oct(ober| |\.)|Nov(ember| |\.)|Dec(ember| |\.)

請注意,在上面的這兩個示例中,May 是一個特殊的例外。在所有的月份中,它是唯一的完整拼寫與 三字母縮寫相同的月份,所以成功的匹配必須包含這兩種變形中的任何一種 作為其縮寫,因此像 “Mayflower”這樣的單詞不會導致誤報。

當匹配模式前面的字符不是空格或行首時,這些示例還是會失敗(返回誤報的結果)。這不太可能會 出現在英語散文中,但是可能出現在程序源代碼中,因為其中可能使用了像 NumOct 這樣的變量名。

要修復這些問題,可以執行下面的操作:

使用圓括號將整個正則表達式括起來,並在它的前面加上另一個限定符,用於匹配行首或者空格字符 ,如下所示(位於一行):

(^| )(Jan(uary| |\.)|Feb(uary| |\.)|Mar(ch| |\.)|Apr(il| |\.)|
May( |\.)|Jun(e| |\.)|Jul(y| |\.)|Aug(ust| |\.)|Sep(tember| |
\.)|Oct(ober| |\.)|Nov(ember| |\.)|Dec(ember| |\.))

另一種完成這個任務的方法是,在該正則表達式的前面加上一個限定符,以匹配非文字數字的字符, 如下所示(位於一行):

([^A-Za-z0-9])(Jan(uary| |\.)|Feb(uary| |\.)|Mar(ch| |\.)|
Apr(il| |\.)|May( |\.)|Jun(e| |\.)|Jul(y| |\.)|Aug(ust| |\.)|
Sep(tember| |\.)|Oct(ober| |\.)|Nov(ember| |\.)|Dec(ember| |\.))

但是仍然存在潛在的問題,對於搜索整篇英文散文,這些示例並不可靠,因為它們可能返回錯誤的匹 配結果,如“Janelle”或“Augury”這樣的單詞。要修復這個問題,您必須使用 單詞圍繞字符將每個月份括起來。

本文開頭提到,正確的正則表達式應該僅返回需要匹配的數據,以便在輸入中排除其他不必要的內容 。這種措詞是經過仔細考慮的,因為對於構建正則表達式來說,這與上下文有關。對於有些情況,前面的 示例非常適合,無需添加額外的單詞圍繞字符。在其他的情況下,可以對其進行相當程度的簡化,例如, 如果您正在搜索僅包含大寫的日期數值數據的日志文件,那麼只需要使用像 [A-S] 這樣的正則表達式來 匹配包含月份名稱的行。

匹配日期

您可以結合一些表 1 所示的數量匹配來匹配日期。

要匹配“month, day, years”,可以使用下面的正則表達式(因為撇號字符是該正則表達 式的一部分,所以必須使用雙引號將它括起來,如下所示):

"[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2})"

這個正則表達式匹配 9 種不同的日期格式:

MONTH [D]D, YY

MONTH [D]D, 'YY

MONTH [D]D, YYYY

MON. [D]D, YY

MON. [D]D, 'YY

MON. [D]D, YYYY

MON [D]D, YY

MON [D]D, 'YY

MON [D]D, YYYY

這個正則表達式的誤報包括“Order 99, 99”,要消除這些誤報,可以將這個正則表達式 與用於月份的正則表達式結合起來,如上所述,以便能夠僅匹配實際的月份名稱。另外,更改數值范圍以 避免錯誤的匹配,並且通過使逗號成為可選項,重復了 18 種可能的格式。

這將得到一個很長的正則表達式。嘗試下面的表達式:

"([^A-Za-z0-9])(Jan(uary| |\.)|Feb(uary| |\.)|Mar(ch| |\.)|
Apr(il| |\.)|May( |\.)|Jun(e| |\.)|Jul(y| |\.)|Aug(ust| |\.)|
Sep(tember| |\.)|Oct(ober| |\.)|Nov(ember| |\.)|
Dec(ember| |\.)) [0-3]?[0-9]{1}(,)? ([0-9]{4}|'?[0-9]{2})"

同樣,根據您的需要仔細設計正則表達式。匹配模式通常比較容易,這是因為它存在於特定輸入的上 下文中,而不是因為它可能獨立於數據集而存在。後代人將會發現,前面那個很長的正則表達式中仍然存 在 Y10K 錯誤,因為它能匹配的最大可能的年份為 9999。

匹配整數

正如您在前幾個示例中看到的,使用方括號中的范圍可以很好地匹配數值。

要匹配任意長度的整數,可以在數值范圍後面加上 +;要包括負值,可以在它的前面加上可選的負號 (連字號)匹配:

-?[0-9]+

前面的例子可以匹配 0,因為 0 是指定范圍中可選的字符。

對於數值匹配,使用圓括號將某些部分括起來也非常有效。要匹配任意的十進制數值,可以使用包含 小數點加上一個或多個數值的可選圍繞字符,以此對前面的正則表達式進行擴展:

-?[0-9]+(\.[0-9]+)?

可以使用方括號指定十進制數值的小數位數。例如,要匹配小數位數為 5 或更多小數位數的正數值, 可以使用下面的表達式:

[^-][0-9]+\.([0-9]){5,}

更多實際的匹配

范圍加上使用括號括起來的元字符,在查找符合任何特定格式的數值時非常有用。將前面介紹的一些 技術結合起來,可以構建匹配各種數據的正則表達式:

要匹配美國的電話號碼,可以使用:

((\([2-9][0-9]{2}\))?\ ?|[2-9][0-9]{2}(?:\-?|\ ?))[2-9][0-9]{2}[- ]?[0-9]{4}

這個正則表達式可以匹配美國 15 種格式的電話號碼:

(NPA) PRE-SUFF

(NPA) PRE SUFF

(NPA) PRESUFF

(NPA)PRE-SUFF

(NPA)PRE SUFF

(NPA)PRESUFF

NPA PRE-SUFF

NPA PRE SUFF

NPA PRESUFF

NPAPRE-SUFF

NPAPRE SUFF

NPAPRESUFF

PRE-SUFF

PRE SUFF

PRESUFF

它還可以匹配美國免費 WATS 號碼,盡管 1-800 的“1-”前綴或其他的免費號碼不是匹配 的一部分,但它本身可以匹配 10 位的數值。對於以 1 或 1+ 開頭的美國號碼和任意數目的空格,也完 全一樣,長途電話撥號前綴本身無法匹配,但是只要它後面跟著實際的號碼,這個正則表達式就能夠將其 找出來。

要匹配兩或三位域的電子郵件地址,可以嘗試下面的表達式:

\<[^@]+\>@[a-zA-Z_\.]+?\.[a-zA-Z]{2,3}

要匹配如今所有流行的 URL,可以使用下面的正則表達式:

(((http(s)?|ftp|telnet|news)://|mailto:)[^\(\)[:space:]]+)

這個表達式可以正常運行,但是匹配 URL 並不像您想象的那麼簡單。匹配任何可能的 URL 的正 則表達式,如 RFC 1738 中的定義,發表在“Regexp for URLs”(請參見參考資料部分)一 文中,它非常巨大並且看起來令人生畏。現在應該將它合並為一個 [:url:] 類(如果有用於處理類似數 據種類的各種新的類,如 [:email:],那就好了)。

結束語

本文涉及到一些用於編寫正則表達式的模式構建技術,以及如何使用它們來完成管理員時常碰到的特 定類型數據匹配的工作。在此過程中,向您介紹了大量有價值的實際正則表達式,您可以將它們添加到自 己的管理工具庫中。

Copyright © Linux教程網 All Rights Reserved