1. 程式人生 > >shell中常用的正則表示式2

shell中常用的正則表示式2

一個正則表示式就是由普通字元(例如字元 a 到 z)以及特殊字元(稱為元字元)組成的文字模式。該模式描述在查詢文字主體時待匹配的一個或多個字串。正則表示式作為一個模板,將某個字元模式與所搜尋的字串進行匹配。

/ hmcLinux聯盟
將下一個字元標記為一個特殊字元、或一個原義字元、或一個 後向引用、或一個八進位制轉義符。例如,'n' 匹配字元 "n"。'/n' 匹配一個換行符。序列 '//' 匹配 "/" 而 "/(" 則匹配 "("。

^ hmcLinux聯盟
匹配輸入字串的開始位置。

$ hmcLinux聯盟
匹配輸入字串的結束位置。

* hmcLinux聯盟
匹配前面的子表示式零次或多次。例如,zo* 能匹配 "z" 以及 "zoo"。 * 等價於{0,}。

+ hmcLinux聯盟
匹配前面的子表示式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等價於 {1,}。

? hmcLinux聯盟
匹配前面的子表示式零次或一次。例如,"do(es)?" 可以匹配 "do" 或 "does" 中的"do" 。? 等價於 {0,1}。

{n} hmcLinux聯盟
n 是一個非負整數。匹配確定的 n 次。例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food" 中的兩個 o。

{n,} hmcLinux聯盟
n 是一個非負整數。至少匹配n 次。例如,'o{2,}' 不能匹配 "Bob" 中的 'o',但能匹配 "foooood" 中的所有 o。'o{1,}' 等價於 'o+'。'o{0,}' 則等價於 'o*'。

{n,m} hmcLinux聯盟
m 和 n 均為非負整數,其中n <= m。最少匹配 n 次且最多匹配 m 次。 "o{1,3}" 將匹配 "fooooood" 中的前三個 o。'o{0,1}' 等價於 'o?'。請注意在逗號和兩個數之間不能有空格。

? hmcLinux聯盟
當該字元緊跟在任何一個其他限制符(*, +, ?, {n}, {n,}, {n,m})後面時,匹配模式是非貪婪的。非貪婪模式儘可能少的匹配所搜尋的字串,而預設的貪婪模式則儘可能多的匹配所搜尋的字串。例如,對於字串"oooo",'o+?' 將匹配單個 "o",而 'o+' 將匹配所有 'o'。

. hmcLinux聯盟


匹配除 "/n" 之外的任何單個字元。要匹配包括 '/n' 在內的任何字元,請使用象 '[./n]' 的模式。

(pattern) hmcLinux聯盟
匹配pattern並獲取這一匹配。所獲取的匹配可以從產生的 Matches 集合得到,在VBScript 中使用 SubMatches 集合,在VisualBasic Scripting Edition 中則使用 $0…$9 屬性。要匹配圓括號字元,請使用 '/(' 或 '/)'。

(?:pattern) hmcLinux聯盟
匹配pattern 但不獲取匹配結果,也就是說這是一個非獲取匹配,不進行儲存供以後使用。這在使用 "或" 字元 (|)來組合一個模式的各個部分是很有用。例如, 'industr(?:y|ies) 就是一個比 'industry|industries'更簡略的表示式。

(?=pattern) hmcLinux聯盟
正向預查,在任何匹配 pattern 的字串開始處匹配查詢字串。這是一個非獲取匹配,也就是說,該匹配不需要獲取供以後使用。例如, 'Windows(?=95|98|NT|2000)' 能匹配 "Windows 2000" 中的 "Windows" ,但不能匹配 "Windows 3.1"中的"Windows"。預查不消耗字元,也就是說,在一個匹配發生後,在最後一次匹配之後立即開始下一次匹配的搜尋,而不是從包含預查的字元之後開始。

(?!pattern) hmcLinux聯盟
負向預查,在任何不匹配Negative lookahead matches the search string at any point where astring not matching pattern的字串開始處匹配查詢字串。這是一個非獲取匹配,也就是說,該匹配不需要獲取供以後使用。例如'Windows(?!95|98|NT|2000)' 能匹配 "Windows 3.1" 中的 "Windows",但不能匹配 "Windows 2000"中的"Windows"。預查不消耗字元,也就是說,在一個匹配發生後,在最後一次匹配之後立即開始下一次匹配的搜尋,而不是從包含預查的字元之後開始。

x|y hmcLinux聯盟
匹配 x 或 y。例如,'z|food' 能匹配 "z" 或 "food"。'(z|f)ood' 則匹配 "zood" 或 "food"。

[xyz] hmcLinux聯盟
字元集合。匹配所包含的任意一個字元。例如, '[abc]' 可以匹配 "plain" 中的 'a'。

[^xyz] hmcLinux聯盟
負值字元集合。匹配未包含的任意字元。例如, '[^abc]' 可以匹配 "plain" 中的'p'。

[a-z] hmcLinux聯盟
字元範圍。匹配指定範圍內的任意字元。例如,'[a-z]' 可以匹配 'a' 到 'z' 範圍內的任意小寫字母字元。

[^a-z] hmcLinux聯盟
負值字元範圍。匹配任何不在指定範圍內的任意字元。例如,'[^a-z]' 可以匹配任何不在 'a' 到 'z' 範圍內的任意字元。

/b hmcLinux聯盟
匹配一個單詞邊界,也就是指單詞和空格間的位置。例如, 'er/b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。

/B hmcLinux聯盟
匹配非單詞邊界。'er/B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。

/cx hmcLinux聯盟
匹配由x指明的控制字元。例如, /cM 匹配一個 Control-M 或回車符。 x 的值必須為 A-Z 或 a-z 之一。否則,將 c 視為一個原義的 'c' 字元。

/d hmcLinux聯盟
匹配一個數字字元。等價於 [0-9]。

/D hmcLinux聯盟
匹配一個非數字字元。等價於 [^0-9]。

/f hmcLinux聯盟
匹配一個換頁符。等價於 /x0c 和 /cL。

/n hmcLinux聯盟
匹配一個換行符。等價於 /x0a 和 /cJ。

/r hmcLinux聯盟
匹配一個回車符。等價於 /x0d 和 /cM。

/s hmcLinux聯盟
匹配任何空白字元,包括空格、製表符、換頁符等等。等價於 [ /f/n/r/t/v]。

/S hmcLinux聯盟
匹配任何非空白字元。等價於 [^ /f/n/r/t/v]。

/t hmcLinux聯盟
匹配一個製表符。等價於 /x09 和 /cI。

/v hmcLinux聯盟
匹配一個垂直製表符。等價於 /x0b 和 /cK。

/w hmcLinux聯盟
匹配包括下劃線的任何單詞字元。等價於'[A-Za-z0-9_]'。

/W hmcLinux聯盟
匹配任何非單詞字元。等價於 '[^A-Za-z0-9_]'。

/xn hmcLinux聯盟
匹配 n,其中 n 為十六進位制轉義值。十六進位制轉義值必須為確定的兩個數字長。例如, '/x41' 匹配 "A"。'/x041' 則等價於 '/x04' & "1"。正則表示式中可以使用 ASCII 編碼。.

/num hmcLinux聯盟
匹配 num,其中 num 是一個正整數。對所獲取的匹配的引用。例如,'(.)/1' 匹配兩個連續的相同字元。

/n hmcLinux聯盟
標識一個八進位制轉義值或一個後向引用。如果 /n 之前至少 n 個獲取的子表示式,則 n 為後向引用。否則,如果 n 為八進位制數字 (0-7),則 n 為一個八進位制轉義值。

/nm hmcLinux聯盟
標識一個八進位制轉義值或一個後向引用。如果 /nm 之前至少有is preceded by at least nm 個獲取得子表示式,則 nm 為後向引用。如果 /nm之前至少有 n 個獲取,則 n 為一個後跟文字 m 的後向引用。如果前面的條件都不滿足,若 n 和 m 均為八進位制數字 (0-7),則 /nm將匹配八進位制轉義值 nm。

/nml hmcLinux聯盟
如果 n 為八進位制數字 (0-3),且 m 和 l 均為八進位制數字 (0-7),則匹配八進位制轉義值 nml。

/un hmcLinux聯盟
匹配 n,其中 n 是一個用四個十六進位制數字表示的 Unicode 字元。例如, /u00A9 匹配版權符號 (?)。

Shell字串處理

  1. 構造字串
    直接構造
    STR_ZERO=hello
    STR_FIRST="i am a string"
    STR_SECOND='success'

    重複多次
    #repeat the first parm($1) by $2 times
    strRepeat()
    {
    local x=$2
    if [ "$x" == "" ]; then
    x=0
    fi

    local STR_TEMP=""
    while [ $x -ge 1 ];
    do
    STR_TEMP=`printf "%s%s" "$STR_TEMP" "$1"`
    x=`expr $x - 1`
    done
    echo $STR_TEMP
    }

    舉例:
    STR_REPEAT=`strRepeat "$USER_NAME" 3`
    echo "repeat = $STR_REPEAT"
  2. 賦值與拷貝
    直接賦值
    與構造字串一樣
    USER_NAME=terry

    從變數賦值
    ALIASE_NAME=$USER_NAME

  3. 聯接
    直接聯接兩個字串
    STR_TEMP=`printf "%s%s" "$STR_ZERO" "$USER_NAME"`
    使用printf可以進行更復雜的聯接
  4. 求長
    求字元數(char)
    COUNT_CHAR=`echo "$STR_FIRST" | wc -m`
    echo $COUNT_CHAR

    求位元組數(byte)
    COUNT_BYTE=`echo "$STR_FIRST" | wc -c`
    echo $COUNT_BYTE

    求字數(word)
    COUNT_WORD=`echo "$STR_FIRST" | wc -w`
    echo $COUNT_WORD
  5. 比較
    相等比較
    str1 = str2

    不等比較
    str1 != str2

    舉例:
    if [ "$USER_NAME" = "terry" ]; then
    echo "I am terry"
    fi

    小於比較
    #return 0 if the two string is equal, return 1 if $1 < $2, else 2strCompare() { local x=0 if [ "$1" != "$2" ]; then x=2 localTEMP=`printf "%s/n%s" "$1" "$2"` local TEMP2=`(echo "$1"; echo "$2") |sort` if [ "$TEMP" = "$TEMP2" ]; then x=1 fi fi echo $x }
  6. 測試
    判空
    -z str

    判非空
    -n str

    是否為數字
    # return 0 if the string is num, otherwise 1
    strIsNum()
    {
    local RET=1
    if [ -n "$1" ]; then
    local STR_TEMP=`echo "$1" | sed 's/[0-9]//g'`
    if [ -z "$STR_TEMP" ]; then
    RET=0
    fi
    fi
    echo $RET
    }

    舉例:
    if [ -n "$USER_NAME" ]; then
    echo "my name is NOT empty"
    fi

    echo `strIsNum "9980"`

  7. 分割
    以符號+為準,將字元分割為左右兩部分
    使用sed
    舉例:
    命令 date --rfc-3339 seconds 的輸出為
    2007-04-14 15:09:47+08:00
    取其+左邊的部分
    date --rfc-3339 seconds | sed 's/+[0-9][0-9]:[0-9][0-9]//g'
    輸出為
    2007-04-14 15:09:47
    取+右邊的部分
    date --rfc-3339 seconds | sed 's/.*+//g'
    輸出為
    08:00

    以空格為分割符的字串分割
    使用awk
    舉例:
    STR_FRUIT="Banana 0.89 100"
    取第3欄位
    echo $STR_FRUIT | awk '{ print $3; }'
  8. 子字串
    字串1是否為字串2的子字串
    # return 0 is $1 is substring of $2, otherwise 1
    strIsSubstring()
    {
    local x=1
    case "$2" in
    *$1*) x=0;;
    esac
    echo $x
    }

模仿秀

您可以將 UNIX 命令列看作是一句話:

  • 可執行命令,如 catls,是動詞——操作。
  • 命令的輸出是名詞——要查閱或使用的資料。
  • Shell 操作符,如 |(管道)或 >(重定向標準輸出),是連詞——用於連線句子。

例如,命令列:ls -A | wc -l 用於計算當前目錄下的條目數(忽略特殊條目 ...),它包含兩個句子。第一個句子 ls -A 是動詞結構,列舉當前目錄下的內容,第二個句子 wc -l 是另一個動詞結構,用於計算行數。第一個句子輸出的結果作為第二個句子的輸入,並由連線詞(管道)連線這兩個句子。

在本系列文章以及其他文章中展示的許多您可能已經學習過的命令列句式都具有這種句子結構。

但是,如果缺少了文法上的修飾語,命令列將顯得不專業。當然,基本句子也能完成工作,但是這樣顯得不優美。(在此對高中英語演唱二人組 Rad 女士和 Perlstein 女士表示歉意。)解決更有趣的問題需要用到形容詞

幾乎所有重要問題都需要從無用資料中過濾出有用資料。雖然屬性的數量和種類會有所不同,但是每種方案都通過某種方式(形式或格式),隱式或顯式地描述了它要查詢並處理的資訊,從而生成另外一種形式的其他資訊。

在命令列中,正則表示式 的作用相當於形容詞——一種描述或限定詞。在應用到輸出時,正則表示式可辨別相關資料和無關資料。

標點概述

讓我們看一個示例問題。

grep 實用工具逐行過濾輸入並尋找匹配。grep 的最簡單應用是列印那些包含與某個模式匹配的文字的行。grep 可以查詢具有固定順序的字元組合,甚至可以通過使用 -i 選項來忽略大小寫。

因此,假定檔案 heroes.txt 包含以下行:

Catwoman
Batman
The Tick
Spider Man
Black Cat
Batgirl
Danger Girl
Wonder Woman
Luke Cage
The Punisher
Ant Man
Dead Girl
Aquaman
SCUD
Spider Woman
Blackbolt
Martian Manhunter

命令列:

grep -i man heroes.txt

將生成:

Catwoman
Batman
Spider Man
Wonder Woman
Ant Man
Aquaman
Martian Manhunter

其中 grep 掃描 heroes.txt 檔案中的每一行並查詢字母 m,後面緊跟 a,然後緊跟 n。除了必須保證相鄰,這些字母可以出現在行的任何位置,甚至可以位於較大的單詞中間。在不考慮大小寫的情況下(-i 選項),Catwoman、Batman、Spider Man、Wonder Woman、Ant Man、Aquaman 和 Martian Manhunter 都包含字串 man

grep 實用工具包含其他可優化搜尋的內建選項。例如,-w 選項限制於匹配整個單詞,因此 grep -i -w man排除 Catwoman 和 Batman(舉例來說)。

該工具還有一個優秀的功能,可以排除而不是包括所有匹配的搜尋結果。使用 -v 選項來排除 匹配的行。例如:

grep -v -i 'spider' heroes.txt

將列印除了包含字串 spider 之外的所有行。

Catwoman
Batman
The Tick
Black Cat
Batgirl
Danger Girl
Wonder Woman
Luke Cage
The Punisher
Ant Man
Dead Girl
Aquaman
SCUD
Blackbolt
Martian Manhunter

但是,對於以下這些情況,您該如何處理?只希望得到那些開頭為“Bat”的單詞;或者以“bat”、“Bat”、“cat”或“Cat”開頭的單詞?或者希望知道有多少漫畫復仇者的名字以“man”結束。在這些例項中,類似於上述三個示例的簡單字串搜尋將無法滿足要求,因為這些搜尋不區分位置。

正則表示式可以 過濾特定的位置,例如行的開始或結束,以及單詞的開始和結束。正則表示式(通常簡寫為 regex)還可以描述:備選項(您可將其稱為“this”或“that”);固定長度、可變長度或不定長度的重複;範圍(例如,“a-m 之間的任意字母”);還有字元的類別或種類(“可列印字元”或“標點符號”),以及其他技術。

表 1 顯示了一些常用的正則表示式操作符。您可以連線表 1 中顯示的元素(以及其他操作符)並加以組合使用,從而構建(非常)複雜的正則表示式。

操作符用途
.(句號) 匹配任意單個字元。
^(脫字號) 匹配出現在行首或字串開始位置的空字串。
$(美元符號) 匹配出現在行末的空字串。
A 匹配大寫字母 A
a 匹配小寫字母 a
/d 匹配任意一位數字。
/D 匹配任意單個非數字字元。
/w 匹配任意單個字母數字字元,同義詞是 [:alnum:]
[A-E] 匹配任意大寫的 A、B、C、DE
[^A-E] 匹配 A、B、C、DE 之外的任意字元。
X? 匹配出現零次或一次的大寫字母 X
X* 匹配零個或任意個大寫 X
X+ 匹配一個或多個字母 X
X{n} 精確匹配 n 個字母 X
X{n,m} 匹配最少 n 個並且不超過 m 個字母 X。如果省略 m,表示式將嘗試匹配最少 n 個 X
(abc|def)+ 匹配一連串的(最少一個) abcdefabcdef 將匹配。

以下是一些使用 grep 作為搜尋工具的正則表示式示例。許多其他 UNIX 工具,包括互動式編輯器 vi 和 Emacs、流編輯器 sedawk,以及所有現代程式語言都支援正則表示式。在您學會正則表示式的語法(也許相當晦澀)之後,就可以將您的專業知識靈活運用到不同的工具、程式語言和作業系統。

要查詢以“Bat”開頭的名稱,請使用:

grep -E '^Bat'

可以使用 -E 選項來指定正則表示式。^(脫字號)字元匹配行首或字串的開頭,這是一個出現在每行或每個字串開頭字元之前的假想字元。字母 Bat 只具有字面含義並且僅匹配那些特定的字元。因此,命令 grep -E '^Bat' 將生成:

Batman
Batgirl

由於許多 regex 操作符也為 Shell 所使用(其中一些具有不同的用途,另外一些則有類似的用途),因此一個好的習慣是使用單引號將命令列中的每個 regex 括起來,以保護 regex 操作符免遭 Shell 的誤解。例如,*(星號)和 $(美元符號)都是 regex 操作符,並且對於您的 Shell 具有特殊的含義。

要查詢以“man”結尾的名稱,可以使用 regex man$ 來匹配序列 man,並且後面緊接與 regex 操作符 $ 匹配的行(字串)。

查詢空行

基於 ^$ 的作用,您可以使用 regex ^$ 來查詢空行(相當於在開始之後立即結束的行)。

要查詢以“bat”、“Bat”、“cat”或“Cat”開頭的單詞,可以使用以下兩個技巧。首先是備選項,如果備選項中的任意 模式匹配,都會產生匹配的結果。例如,命令:

grep -E '^(bat|Bat|cat|Cat)' heroes.txt

可實現這一技巧。regex 操作符 |(豎線)表示備選項,因此 this|that 匹配字串 this 或字串 that。因此,^(bat|Bat|cat|Cat) 表示“行首緊跟 batBatcatCat之一。”當然,可以使用 grep -i 來簡化該 regex,這樣可以忽略大小寫,從而將命令簡化為:

grep -i -E '^(bat|cat)' heroes.txt

匹配“bat”、“Bat”、“cat”或“Cat”的另一個方法是使用 [ ](方括號)集合 操作符。如果將一組字元放在一個集合中,則可以匹配那些字元中的任意一個。(您可以將集合 看作是字元備選項的簡寫法。)

例如,命令列:

grep -E '^[bcBC]at' heroes.txt

與以下命令生成的結果相同:

grep -E '^(bat|Bat|cat|Cat)' heroes.txt

您可以再次使用 -i 將 regex 簡化為 ^[bc]at

而且,還可以使用 -(連字元)操作符在集合中指定包含的字元範圍。例如,使用者名稱通常以字母開頭。假定要在提交給您的伺服器的 Web 表格中驗證這樣的使用者名稱,可以使用類似於 ^[A-Za-z] 的 regex。此 regex 表示“字串的開頭後緊跟任意大寫字母 (A-Z) 或任意小寫字母 (a-z)。”順便說明一下,[A-z][A-Za-z] 作用相同。

還可以在集合中混合使用範圍和單個字元。regex [A-MXYZ] 將匹配任意大寫的 A-M、X、YZ

並且,如果希望反轉集合(即排除集合中的任意字元),可以使用特殊集合 [^ ] 幷包含要排除的範圍或字元。以下是反轉集合的示例。要查詢所有名稱中包含 at 的超級英雄,並排除 Dark Knight 和 Batman,請鍵入:

grep -i -E '[^b]at' heroes.txt

此命令生成:

Catwoman
Black Cat

由於某些集合需要經常使用,所以設計出簡化符號以代替大量字元。例如,集合 [A-z0-9_] 十分常用,因此可以簡寫為 /w。與此類似,操作符 /W 是集合 [^A-z0-9_] 的簡寫。還可以使用符號 [:alnum:] 代替 /w,使用 [^[:alnum:]] 代替 /W

順便說明一下,/w(以及同義詞 [:alnum:])是特定於區域的,而 [A-z0-9_] 即表示字母 A-z、數字 0-9 和下劃線。如果要開發國際化應用程式,請使用區域特定的格式以使程式碼可以在許多區域之間移植。

到目前為止,已經介紹了字面值、位置和兩種備選項操作符。僅使用這些內容,就可以匹配大多數具有可預測 長度的模式。現在回到使用者名稱,通過以下 regex 命令可以確保每個使用者名稱以字母開頭並緊跟恰好七個字母或數字:

[a-z][a-z0-9][a-z0-9][a-z0-9][a-z0-9][a-z0-9][a-z0-9][a-z0-9]

但是這樣有點笨拙。而且,它只匹配恰好八個字元的使用者名稱。它不會匹配三到八個字元之間的名稱,這通常也是有效的使用者名稱。

正則表示式還可以包括重複修飾符。重複修飾符可以指定數量,如沒有、一個、多個、一個或多個,零或一個、五到十個,以及恰好三個。重複修飾符必須與其他模式組合,修飾符本身沒有含義。

例如,regex:

^[A-z][A-z0-9]{2,7}$

可以實現前面描述的使用者名稱過濾功能。使用者名稱 是以字母開頭,後面緊跟至少兩個,但不超過七個字母或數字的字串,並且緊跟字串結尾。

此處的位置定位點非常重要。如果沒有兩個位置操作符,則會錯誤地接受任意長度的使用者名稱。為什麼呢?請考慮 regex:

^[A-z][A-z0-9]{2,7}

此命令辨別:字串是否以字母開頭並緊跟二到七個字母?但是它未提到終止條件。因此,字串 samuelclemens 滿足條件,但是它的長度顯然超出了有效使用者名稱的範圍。與此類似,省略開始定位點 ^,或同時省略兩個定位點將分別匹配以類似 munster1313 結束或包含該字串的字串。如果必須匹配特定的長度,請記得在要求的模式的開頭和結尾分別加上分隔符。

以下是其他一些示例:

  • 可以使用 {2,} 查詢兩次或多次重複。regex ^G[o]{2,}gle 匹配 GoogleGooogleGoooogle 等等。
  • 重複修飾符 ?+* 分別查詢零次或一次、一次或多次,以及零次或多次重複。(例如,您可以將 ? 看作是 {0,1} 的簡寫法。)

    regex boys? 匹配 boyboys;regex Goo?gle 匹配 GogleGoogle

    regex Goo+gle 匹配 GoogleGooogleGoooogle 等等。

    construct Goo*gle 匹配 GogleGoogleGooogle 等等。

  • 可以將重複修飾符應用到單個字元(如上所示),還可以應用到更復雜的組合。使用 () 圓括號(就像數學中的用法)將修飾符應用到子表示式。下面是一個示例:給定文字檔案 test.txt:
    The rain in Spain falls mainly 
    on the the plain.

    It was the best of of times;
    it was the worst of times.

    命令 grep -i -E '(/b(of|the)/W+){2,}' test.txt 將生成:

    on the the plain.
    It was the best of of times;
  • regex 操作符 /b 匹配單詞邊界(/W/w|/w/W)。該 regex 表示“一連串完整單詞‘the’或‘of’後面緊跟非文字字元。”您可能會提出疑問,為什麼 /W+ 是必需的:/b 是位於單詞開頭或結尾的空字串。在單詞之間必須包括這一(或這些)字元,否則該 regex 將無法找到匹配。

查詢文字是常見的問題,但是更常見的問題則是希望在找到文字之後將其提取出來。換句話說,您希望去粗取精。

正則表示式通過捕獲 來提取資訊。如果希望將需要的文字與其他內容分開,請使用圓括號將模式括起來。實際上,您已經使用圓括號收集術語;在預設情況下,圓括號自動進行捕獲。

要檢視捕獲,請切換到 Perl。(grep 實用工具不支援捕獲,因為其目標是列印包含模式的行。)

以下命令:

perl -n -e '/^The/s+(.*)$/ && print "$1/n"' heroes.txt

將列印:

Tick
Punisher

使用命令 perl -e 可以直接從命令列執行 Perl 程式。perl -n 命令針對輸入檔案的每一行執行一次程式。命令的 regex 部分,即位於斜槓之間的文字(/)表示“匹配字串的開頭,然後字母‘T’、‘h’、‘e’後緊跟一個或多個空格字元 /s+,然後捕獲直到字串結尾的所有字元。

Perl 捕獲內容被放在以 $1 開頭的特殊 Perl 變數中。Perl 程式的其餘部分列印捕獲的內容。

每個巢狀的括號對,從左開始算起,每個左圓括號加一,放在下一個特殊的數字變數中。例如:

perl -n -e '/^(/w)+-(/w+)$/ && print "$1 $2"'

將生成:

Spider Man
Ant Man
Spider Woman

捕獲感興趣的文字僅僅是隔靴搔癢。如果能夠準確確定材料,就可以使用其他材料改變其外觀。類似於 vi 和 Emacs 的編輯器將模式匹配與替換組合,從而將查詢和替換文字組合成一步操作。還可以使用模式、替換和 sed 從命令列更改文字。

豐富的主題

正則表示式非常強大;可供使用的操作符的數量龐大,種類繁多。它包含如此豐富的資訊和實踐知識,我們在這裡所能列舉的實屬鳳毛麟角。

幸運的是,有以下三種優秀的正則表示式理論來源可供使用:

  • 如果在您的系統上有 Perl,可以參閱 Perl Regular Expression man 頁面(鍵入 perldoc perlre)。它會提供 regex 的精彩介紹,幷包含許多有用的示例。許多程式語言都已採用 Perl 相容的正則表示式 (PCRE),因此您在此 man 頁面讀到的內容已被直接轉換到 PHP、Python、Java™ 和 Ruby 程式語言,以及許多其他最新工具。
  • Jeffrey Friedl 編著的《正則表示式》(第三版)被認為是 regex 用法方面的聖經。該書細緻、準確、清晰、務實地說明了匹配的工作方式、所有的 regex 操作符、多數優先性(限制 +* 匹配字元的數量),以及更多內容。此外,Friedl 的書還包括一些令人驚歎的正則表示式,可以準確地匹配完全限定的電子郵件地址和其他 Request for Comments (RFC) 特定的字串。
  • Nathan Good 編著的 Regular Expression Recipes 一書提供了針對許多常見資料處理和過濾問題的有用的解決方案。如果需要提取郵政編碼、電話號碼或引用的字串,請嘗試 Nathan 的解決方案。

在命令列中,可以採用許多方法使用正則表示式。幾乎每個處理文字的命令都支援某種形式的正則表示式。大多數 Shell 命令語法還或多或少地擴充套件正則表示式以匹配檔名(儘管操作符的功能可能有所不同)。

例如,鍵入 ls [a-c] 以查詢名為 a、bc 的檔案。鍵入 ls [a-c]* 以查詢以 a、bc 開頭的所有檔名。此處的 * 在 Shell 中不像 grep 的直譯器那樣修飾 [a-c]* 被解釋為 .*? 操作符在 Shell 中也可以工作,但是被解釋為 .,即匹配任意單個字元。

檢視您最喜歡的實用工具或 Shell 的文件以確定哪些 regex 操作符受支援,以及操作符可能具有的獨特性。

下面我們就以具體的例項來看一下如何使用正則表示式。其中用黑體著重標出的是匹配到的字串。

一個最簡單的例子便是 /all/,比如下面一段文字:

John’s ball fell into the hole

John cried because it is all his life.

這個正則表示式不含任何的原字元,它查詢的是字串all,這個字串all可以是獨成一個單詞,也可以是其它單詞的一部分,因此正則表示式/all/既匹配ball裡的all,也匹配完整的單詞all。

下面我們著重討論正則表示式裡原字元的用法。

3.1            行首、行尾定位符

行首定位符^

Here is a tongue twister:

Bobby Bippy bought a bat.

Bobby Bippy bought a ball.
With his bat Bob banged the ball
Banged it bump against the wall
But so boldly Bobby banged it
That he burst his rubber ball, "Boo!" cried Bobby
Bad luck ball, Bad luck Bobby, bad luck ball
Now to drown his many troubles
Bobby Bippy's blowing bubbles.

/^Bobby/

匹配位於行首的Bobby。

Here is a tongue twister:

Bobby Bippy bought a bat.

Bobby Bippy bought a ball.
With his bat Bob banged the ball
Banged it bump against the wall
But so boldly Bobby banged it
That he burst his rubber ball, "Boo!" cried Bobby
Bad luck ball, Bad luck Bobby, bad luck ball
Now to drown his many troubles
Bobby Bippy's blowing bubbles.

/Bobby$/

匹配位於行尾的Bobby。

3.2            詞首、詞尾定位符

詞首定位符 /<

Here is a tongue twister:

Bobby Bippy bought a bat.

Bobby Bippy bought a ball.
With his bat Bob banged the ball
Banged it bump against the wall
But so boldly Bobby banged it
That he burst his rubber ball, "Boo!" cried Bobby
Bad luck ball, Bad luck Bobby, bad luck ball
Now to drown his many troubles
Bobby Bippy's blowing bubbles.

//<Bo/

匹配位於詞首的字串Bo。

詞尾定位符 />

Here is a tongue twister:

Bobby Bippy bought a bat.

Bobby Bippy bought a ball.
With his bat Bob banged the ball
Banged it bump against the wall
But so boldly Bobby banged it
That he burst his rubber ball, "Boo!" cried Bobby
Bad luck ball, Bad luck Bobby

Bad luck ball
Now to drown his many troubles
Bobby Bippy's blowing bubbles.

/ball/>/

匹配位於詞尾的字串ball。

在一個表示式中搭配使用詞首定位符與詞尾定位符

John’s ball fell into the hole

John cried because it is his whole life

//<hole/>/

匹配以h作為單詞開頭並且以e作為單詞結尾的模式hole。也就是說,字母h的前面是一個分隔單詞的字元(比如空格或換行符),字母l的後面也是一個分隔單詞的字元。這樣,在這個例子中只有完整的單詞hole會被匹配,而單詞whole就不會被匹配。

3.3            匹配單個字元

匹配任意的一個字元 .

Here is a tongue twister:

Bobby Bippy bought a bat.

Bobby Bippy bought a ball.
With his bat Bob banged the ball
Banged it bump against the wall
But so boldly Bobby banged it
That he burst his rubber ball, "Boo!" cried Bobby
Bad luck ball, Bad luck Bobby, bad luck ball
Now to drown his many troubles
Bobby Bippy's blowing bubbles.

/By/

匹配B開頭後面緊跟三個任意字元,最後緊接著一個y的字串。在這個例子中,Bobby和Bippy都會被匹配。

匹配0個或多個前一字元 *

Here is a tongue twister:

Bobby Bippy bought a bat.

Bobby Bippy bought a ball.
With his bat Bob banged the ball
Banged it bump against the wall
But so boldly Bobby banged it
That he burst his rubber ball, "Boo!" cried Bobby
Bad luck ball, Bad luck Bobby, bad luck balll
Now to drown his many troubles
Bobby Bippy's blowing bubbles.

/ al*/

這裡的星號(*)匹配0個或多個在它前面的那個字元。前面曾提到過,正則表示式裡的*和shell裡的*作用是截然不同的。在shell裡*表示任意個數的任意字元,而在正則表示式裡,*只代表任意個數(包括0個)的前一字元,*可以看作和它前面那個字元是粘連在一起的,*只限制它前面那一個字元。這個正則表示式中的*匹配單獨一個或多個連續的l,甚至也匹配一個l也沒有的模式,所以,單個字元a也會被匹配。

3.4            匹配多個字元

匹配一組字元裡的任意字元 [ ]

Here is a tongue twister:

Bobby Bippy bought a bat.

Bobby Bippy bought a ball.
With his bat Bob banged the ball
Banged it bump against the wall
But so boldly Bobby banged it
That he burst his rubber ball, "Boo!" cried Bobby
Bad luck ball, Bad luck Bobby, bad luck balll
Now to drown his many troubles
Bobby Bippy's blowing bubbles.

/[bw]all/

方括號匹配一組字元中的一個,這個正則表示式查詢的是第一個字母是b或w,後面緊跟著all的字串,因此在這個例子中,wall和ball都會被匹配。

匹配指定範圍內的字元 [x-y]

Here is a tongue twister:

Bobby Bippy bought a bat.

Bobby Bippy bought a ball.
With his bat Bob banged the ball
Banged it bump against the wall
But so boldly Bobby banged it
That he burst his rubber ball, "Boo!" cried Bobby
Bad luck ball, Bad luck Bobby, bad luck ball
Now to drown his many troubles
Bobby Bippy's blowing bubbles.

/B[a-z]p/

方括號裡的短線(-)匹配某一範圍內的一個字元,這個正則表示式將查詢第一個字母是B,第二個字母是ASCII碼介於a到z的字元(小寫字母),第三個字母是p的字串。

匹配不在指定範圍內的字元 [^ ]

Here is a tongue twister:

Bobby Bippy bought a bat.

Bobby Bippy bought a ball.

With his bat Bob banged the ball
Banged it bump against the wall
But so boldly Bobby banged it
That he burst his rubber ball, "Boo!" cried Bobby
Bad luck ball, Bad luck Bobby, bad luck ball
Now to drown his many troubles
Bobby Bippy's blowing bubbles.

/all[^A-Z0-9]/

方括號內的脫字元^是一個否定字元,這個正則表示式查詢的是後面帶一個特殊字元的all,這個特殊字元既不是小寫字母又不是大寫字母,也不是0到9的數字,比如它可以是一個標點符號或空格。

根據字元x出現的次數匹配 x/{m/}  x/{m,/}  x/{m, n/}

比如這個正則表示式:/Go/{2,5/}gle/將匹配G後面至少出現2個,最多有不超過5個o的模式。Google,Goooogle會被匹配,而Gogle和Goooooogle則不會被匹配。

3.5            轉義字元

如果要匹配的字串中含有正則表示式的原字元,需要用斜線將其轉義,就像c語言裡列印單引號 ’ 要寫成 /’ 一樣。這裡有個例子:我們想要查詢字串google.com,要查詢的字串裡含有正則表示式的原字元“.”,因此這個正則表示式要寫成 /google/.com/,如果不用 / 轉義,找到的將是google後面跟一個任意的字元,然後跟一個com的字串。這顯然不一定是我們要找的。

3.6            字元標籤

例如在下面一段文字裡:

Occurence and happening are the most general. I mean, the words occurence and happening are most generally used.

在這段文字裡有兩個拼錯的單詞,Occurence和occurence,(其實應該是occurrence),我們可以在vi中用下面的表示式將其修改:

:1,$s//([Oo]ccur/)ence//1rence/

我們且不管這個vi命令的用法(其實它是一個替換命令,我們在後面介紹sed時還將提到)我們先拿出這個語句中的兩個表示式:

//([Oo]ccur/)ence/

/1rence

其中前一個是一個正則表示式。這個命令用後面的表示式內容替換前面的正則表示式匹配到的內容。vi編輯器將查詢單詞Occurence和occurence,如果找到,就把圓括號中的內容加上標籤(Occur或occur被加上標籤),因為這是第一個被標記的模式,所以被稱為標籤1。這個模式被儲存在稱為暫存器1的記憶體暫存器中。在第二個正則表示式中用/1引用暫存器1中的內容,/1被替換為暫存器中的內容,後面緊跟一個rence,於是,拼錯的Occurence和occurence被改正為正確的Occurrence和occurrence。

3.7            原字元組合使用的例子

例1://<Bob.*all/>/

Here is a tongue twister:

Bobby Bippy bought a bat.

Bobby Bippy bought a ball.
With his bat Bob banged the ball
Banged it bump against the wall
But so boldly Bobby banged it
That he burst his rubber ball, "Boo!" cried Bobby
Bad luck ball, Bad luck Bobby, bad luck ball
Now to drown his many troubles
Bobby Bippy's blowing bubbles.

//<Bob.*all/>/

匹配以Bob開頭,後面跟任意個數的任意字元,然後以all結尾的字串。這裡再次重複, * 在Shell裡表示任意個數的任意字元,而在正則表示式裡表示任意個數的前一字元。與 . 配合使用表示任意個數(包括零個)的任意字元。實際上,* 也可以表示重複零次或任意次它前面的一組字元,我們稱這一組(有時也可能是一個)字元為“原子”。當原子包括多個字元時,這多個字元要用圓括號括起來,並且需要將圓括號轉義;當原子只含一個字元時,可以不用圓括號。在這個例子裡,. 表示一個任意字元,緊跟著一個*表示重複0次或任意次前面的那個任意字元。而下面的例子

//(sup/)*info/

則表示匹配在字串info前有0個或多個sup的字串,因此 supinfo, info, supsupinfo都會被匹配。

例2:/B[a-z][bp]*y$/

Here is a tongue twister:

Bobby Bippy bought a bat.

Bobby Bippy bought a ball.
With his bat Bob banged the ball
Banged it bump against the wall
But so boldly Bobby banged it
That he burst his rubber ball, "Boo!" cried Bobby
Bad luck ball, Bad luck Bobby, bad luck ball
Now to drown his many troubles
Bobby Bippy's blowing bubbles.

/B[a-z][bp]*y$/

這個正則表示式匹配這樣的字串:開頭字元是B,第二個字母是一個小寫字母,後面緊跟0個或多個重複的b或p,最後跟一個y,並且這個字串位於行的末尾。