【Linux 命令行與 shell 腳本編程大全】20 正則表達(dá)式

導(dǎo)覽

  • 正則表達(dá)式模式
    ^:錨定行首
    $:錨定行尾
    .:特殊字符點(diǎn)號(hào)用來匹配換行符之外的任意單個(gè)字符,至少一個(gè)字符。
    []:字符組,配置方括號(hào)內(nèi)的字符或數(shù)字,[ch],[Yy]
    [^]:不包含字符組內(nèi)的字符,[^ch]
    區(qū)間:[c-h],[0-9]匹配c到h之間的單詞

  • 特殊字符組:
    [[:alpha:]] 匹配任意字母字符,不管是大寫還是小寫
    [[:alnum:]] 匹配任意字母數(shù)字字符0~9、 AZ或az
    [[:blank:]]匹配空格或制表符
    [[:digit:]]匹配0~9之間的數(shù)字
    [[:lower:]] 匹配小寫字母字符a~z
    [[:print:]] 匹配任意可打印字符
    [[:punct:]] 匹配標(biāo)點(diǎn)符號(hào)
    [[:space:]] 匹配任意空白字符:空格、制表符、 NL、 FF、 VT和CR
    [[:upper:]]匹配任意大寫字母字符A~Z
    *:匹配模式的文本中出現(xiàn)0次或多次

  • 擴(kuò)展的正則表達(dá)式(gawk)
    ?:匹配前面的字符出現(xiàn)0次或1次 
    +:配置前面的字符出現(xiàn)1次或多次
    {m}:配置至少出現(xiàn)m次
    {,n}:配置至多出現(xiàn)n次    
    {m,n}:配置至少m次,至多n次
    | 用邏輯OR方式指定正則表達(dá)式引擎要用的兩個(gè)或多個(gè)模式。
    ():表達(dá)式分組,正則表達(dá)式模式也可以用圓括號(hào)進(jìn)行分組。當(dāng)你將正則表達(dá)式模式分組時(shí),該組會(huì)被視為一個(gè)標(biāo)準(zhǔn)字符??梢韵駥?duì)普通字符一樣給該組使用特殊字符
    --re- interval默認(rèn)情況下, gawk程序不會(huì)識(shí)別正則表達(dá)式間隔。必須指定gawk程序的--re- interval命令行選項(xiàng)才能識(shí)別正則表達(dá)式間隔。

20.1 什么是正則表達(dá)式

20.1.1 定義

正則表達(dá)式是你定義的、Linux工具用來過濾文本的模式模板。正則表達(dá)式模式利用通配符來代表數(shù)據(jù)流中的一個(gè)或者多個(gè)字符

image

20.1.2 正則表達(dá)式類型

在Linux中,有兩種流行的正則表達(dá)式引擎:

  • POSIX基礎(chǔ)正則表達(dá)式(BRE)引擎 sed
  • POSIX擴(kuò)展正則表達(dá)式(ERE)引擎 gawk

20.2 定于BRE模式

20.2.1 純文本

[ttxie@41 ~]$ echo "This is a test" |sed -n '/test/p'
This is a test
[ttxie@41 ~]$ echo "This is a test" |sed -n '/trial/p'
[ttxie@41 ~]$ echo "This is a test" |awk '/test/{print $0}'
This is a test
[ttxie@41 ~]$ echo "This is a test" |awk '/trial/{print $0}'

正則表達(dá)式并不關(guān)心模式在數(shù)據(jù)流中的位置,也不關(guān)心出現(xiàn)了多少次,只要匹配了就會(huì)將該字符串傳會(huì)linux工具。

正則表達(dá)式模式區(qū)分大小寫。

[ttxie@41 ~]$ echo "This is a test" |sed -n '/this/p'
[ttxie@41 ~]$ echo "This is a test" |sed -n '/This/p'
This is a test

模糊匹配

只要定義的文本出現(xiàn)在數(shù)據(jù)流中,正則就能匹配,反之則不成立

[ttxie@41 ~]$ echo "The books are expensive" |sed -n '/book/p'
The books are expensive

[ttxie@41 ~]$ echo "The book are expensive" |sed -n '/books/p'

20.2.2 特殊字符

正則表達(dá)式識(shí)別的特殊字符包括:.*[]^${}\+?|(),如果要用某個(gè)特殊字符作為文本字符,就必須轉(zhuǎn)義。在前面加上反斜線\。

[ttxie@41 ~]$ echo "This cost is \$200" |sed -n '/\$/p'
This cost is $200

要使用正斜線也需要用轉(zhuǎn)義字符

[ttxie@41 ~]$ echo "3 / 2" |sed '/\//p'
3 / 2

20.2.3 錨字符 ^ $

默認(rèn)情況下,模式出現(xiàn)再數(shù)據(jù)流中的任何地方,它就能匹配。
有兩個(gè)特殊字符可以用來將模式鎖定在數(shù)據(jù)流中的行首或行尾。

20.2.3.1.鎖定在行首(脫字符 ^)

^ 定義從數(shù)據(jù)流中文本行的行首開始的模式。如果模式出現(xiàn)在行首之外的位置,正則表達(dá)式模式則無法匹配。
需要用^,就必須將它放在正則表達(dá)式中指定的模式前面。

[ttxie@41 ~]$ echo "The book store" |sed -n '/^book/p'
[ttxie@41 ~]$ echo "Books are great" |sed -n '/^Book/p'
Books are great

還可以輸入文件:

$sed -n ‘/^this/p’ data.txt

data.txt 中以this開頭的行就能找出來。
注意如果將^放到模式開頭之外的其他位置,那么就跟普通字符一樣了。

[ttxie@41 ~]$ echo "This is ^ test" |sed -n '/s ^/p'
This is ^ test

注意:
如果指定正則表達(dá)式模式時(shí)只用了脫字符,就不需要用反斜線來轉(zhuǎn)義。
如果你在模式中先指定了脫字符,隨后還有一些其他文本,那么你必須在脫字符前用轉(zhuǎn)義字符。

20.2.3.2. 鎖定在行尾

用美元符$

[ttxie@41 ~]$ echo "This is a good book" |sed -n '/book$/p'
This is a good book
[ttxie@41 ~]$ echo "This is a good book" |sed -n '/good$/p'
[ttxie@41 ~]$ echo "This is a good book" |sed -n '/books$/p'  

要想匹配,文本模式必須是行的最后一部分。

20.2.3.3. 組合錨點(diǎn)

  1. 比如想到匹配指定內(nèi)容
    $sed ‘/^this is test line$/p’ data.txt 匹配行 this is test line

  2. 將兩個(gè)錨點(diǎn)直接組合在一起,之間不加任何東西,這樣就過濾出數(shù)據(jù)流中的空白行。

$sed -n ‘/^$/p’ data.txt 這樣可以把data.txt 中的空白行過濾出來。

  1. 過濾掉空白行
[ttxie@41 part20]$ cat data5.txt 
This is one line

this is another line

[ttxie@41 part20]$ sed '/^$/d' data5.txt 
This is one line
this is another line

20.2.4 點(diǎn)號(hào)字符 .

用來匹配除換行符之外的任意單個(gè)字符。它必須匹配一個(gè)字符,如果點(diǎn)字符的位置沒有字符那么模式就不成立。

[ttxie@41 part20]$ cat data6.txt 
this is a test line
the cat is sleeping
that is a very nice hat
this test is at line four
at ten o'clock we'll go home

[ttxie@41 part20]$ sed -n '/.at/p' data6.txt
the cat is sleeping
that is a very nice hat
this test is at line four

第4行空格也算一個(gè)字符。注意第5行沒有匹配到。at前面沒有字符了。

20.2.5 字符組 []

可以限定待匹配的具體字符,在正則表達(dá)式中,這稱為字符組。用[]括起來

[ttxie@41 part20]$ cat data6.txt 
this is a test line
the cat is sleeping
that is a very nice hat
this test is at line four
at ten o'clock we'll go home

[ttxie@41 part20]$ sed -n '/[ch]at/p' data6.txt 
the cat is sleeping
that is a very nice hat

相當(dāng)于只匹配cat 或者 hat。其他的at就不匹配了。

在不確定大小寫的時(shí)候,字符組會(huì)非常有用:

[ttxie@41 part20]$ echo "yes" |sed -n '/[Yy]es/p'
yes
[ttxie@41 part20]$ echo "Yes" |sed -n '/[Yy]es/p'
Yes

還可以用多個(gè)字符組:

[ttxie@41 part20]$ echo "Yes" |sed -n '/[Yy][Ee][Ss]/p'
Yes
[ttxie@41 part20]$ echo "yEs" |sed -n '/[Yy][Ee][Ss]/p'
yEs
[ttxie@41 part20]$ echo "yES" |sed -n '/[Yy][Ee][Ss]/p'
yES

這樣就相當(dāng)于可以限制行的字符個(gè)數(shù)和區(qū)間了

20.2.6 排除型字符組

相當(dāng)于字符組取反,可以尋找字符組中沒有的字符。在前面加個(gè)脫字符就好了。

[ttxie@41 part20]$ cat data6.txt 
this is a test line
the cat is sleeping
that is a very nice hat
this test is at line four
at ten o'clock we'll go home

[ttxie@41 part20]$ sed -n '/[^ch]at/p' data6.txt 
this test is at line four

匹配出c和h以外的字符。由于空格字符屬于這個(gè)范疇,所以它通過了模式匹配,但即使是排除,字符組必須匹配一個(gè)字符,所以以at為開頭的行仍然未能通過匹配。

20.2.7 區(qū)間

  1. 0 – 9,可以直接這么寫[0-9] 而不需要[0123456789]
[ttxie@41 part20]$ cat data_num.txt 
60633
46201
223001
4353
22203
[ttxie@41 part20]$ sed -n '/[0-9][0-9][0-9][0-9][0-9]/p' data_num.txt 
60633
46201
223001
22203
[ttxie@41 part20]$ sed -n '/^[0-9][0-9][0-9][0-9][0-9]$/p' data_num.txt 
60633
46201
22203

第一種:它成功過濾掉了過短的數(shù)字,但是因?yàn)樽詈笠粋€(gè)字符組沒有字符可匹配,它也通過了6位數(shù)字的;
第二種:指明它的行首和行尾,可以確保只匹配五位數(shù)。

  1. 字母也可以
    匹配c到h這個(gè)區(qū)間的字符。
[ttxie@41 part20]$ cat data6.txt 
this is a test line
the cat is sleeping
that is a very nice hat
this test is at line four
at ten o'clock we'll go home

[ttxie@41 part20]$ sed -n '/[c-h]at/p' data6.txt 
the cat is sleeping
that is a very nice hat

還可以指定多個(gè)不連續(xù)的區(qū)間:指定 a-c 和 h-m區(qū)間的字母

[ttxie@41 part20]$ sed -n '/[a-ch-m]at/p' data6.txt 
the cat is sleeping
that is a very nice hat

20.2.8 特殊的字符組

除了自定義的區(qū)間(比如[0-9] [a-f])之外,BRE還包含了一些特殊的字符組。見下表

image.png

使用:

[ttxie@41 part20]$ echo "abc" |sed -n '/[[:digit:]]/p'
[ttxie@41 part20]$ echo "abc" |sed -n '/[[:alpha:]]/p'
abc
[ttxie@41 part20]$ echo "abc123" |sed -n '/[[:digit:]]/p'
abc123
[ttxie@41 part20]$ echo "This is , a test" |sed -n '/[[:punct:]]/p'
This is , a test

20.2.9 星號(hào) *

在字符后面放置星號(hào)用來表明該字符必須在匹配模式的文本中出現(xiàn)0次多次

[ttxie@41 part20]$ echo "ik" |sed -n '/ie*k/p'   ##出現(xiàn)0次
ik
[ttxie@41 part20]$ echo "iek" |sed -n '/ie*k/p'
iek
[ttxie@41 part20]$ echo "ieek" |sed -n '/ie*k/p'
ieek

*還能用到字符組上,它允許指定可能在文本中出現(xiàn)多次的字符組或區(qū)間:

[ttxie@41 part20]$ echo "bt" |sed -n '/b[ae]*t/p'
bt
[ttxie@41 part20]$ echo "bat" |sed -n '/b[ae]*t/p'
bat
[ttxie@41 part20]$ echo "bet" |sed -n '/b[ae]*t/p'
bet
[ttxie@41 part20]$ echo "btt" |sed -n '/b[ae]*t/p'
btt
[ttxie@41 part20]$ echo "baat" |sed -n '/b[ae]*t/p'
baat
[ttxie@41 part20]$ echo "baaeeeet" |sed -n '/b[ae]*t/p'
baaeeeet
[ttxie@41 part20]$ echo "baaeeaeet" |sed -n '/b[ae]*t/p'
baaeeaeet
[ttxie@41 part20]$ echo "baaekaeet" |sed -n '/b[ae]*t/p'
[ttxie@41 part20]$ echo "baaetaeet" |sed -n '/b[ae]*t/p'
baaetaeet

出現(xiàn)t也行?

20.3 擴(kuò)展正則表達(dá)式(POSIX ERE)

提供了一些可以供linux應(yīng)用和工具使用的額外符號(hào)。

gawk程序能夠識(shí)別ERE模式(會(huì)慢一點(diǎn)),sed編輯器不能識(shí)別(查找比較快)。

20.3.1 問號(hào)?

類似于星號(hào),但是有點(diǎn)不同。

  1. 問號(hào)表明前面的字符可以出現(xiàn)0次或1次,不會(huì)匹配出現(xiàn)多次的字符。
[ttxie@41 part20]$ echo "bt" |gawk '/be?t/{print $0}'
bt
[ttxie@41 part20]$ echo "bet" |gawk '/be?t/{print $0}'
bet
[ttxie@41 part20]$ echo "beeet" |gawk '/be?t/{print $0}'   #e 出現(xiàn)了2次,這里就不輸出了
  1. 還可以跟字符組一起使用:
[ttxie@41 part20]$ echo "bt" |gawk '/b[ae]?t/{print $0}'
bt
[ttxie@41 part20]$ echo "bat" |gawk '/b[ae]?t/{print $0}'
bat
[ttxie@41 part20]$ echo "bet" |gawk '/b[ae]?t/{print $0}'
bet
[ttxie@41 part20]$ echo "baet" |gawk '/b[ae]?t/{print $0}'
[ttxie@41 part20]$ echo "baat" |gawk '/b[ae]?t/{print $0}'
[ttxie@41 part20]$ echo "bot" |gawk '/b[ae]?t/{print $0}'

如果字符組中的字符出現(xiàn)了0次或1次,模式匹配就成立,但如果兩個(gè)字符都出現(xiàn)了,或者一個(gè)字符出現(xiàn)了2次,模式匹配就不成立。

20.3.2 加號(hào)+

有點(diǎn)像*號(hào)。但是必須出現(xiàn)1次以上。可以有多次,但必須至少出現(xiàn)1次。

[ttxie@41 part20]$ echo "bt" |gawk '/be+t/{print $0}'
[ttxie@41 part20]$ echo "bet" |gawk '/be+t/{print $0}'
bet
[ttxie@41 part20]$ echo "beeeet" |gawk '/be+t/{print $0}'
beeeet

20.3.3 使用花括號(hào){}

花括號(hào)允許你為可重復(fù)的正則表達(dá)式指定一個(gè)上限,這通常稱為間隔??梢杂脙煞N格式來指定區(qū)間。

1)m:正則表達(dá)式準(zhǔn)確出現(xiàn)m次
2)m,n:正則表達(dá)式至少出現(xiàn)m次,至多n次。

注意:默認(rèn)情況下gawk程序不識(shí)別正則表達(dá)式間隔。必須指定gawk程序的–re-interval命令行選項(xiàng)才能識(shí)別正則表達(dá)式間隔。

[ttxie@41 part20]$ echo "bet" |gawk --re-interval '/be{1}t/{print $0}'
bet
[ttxie@41 part20]$ echo "bet" |gawk --re-interval '/be{1,2}t/{print $0}'
bet
[ttxie@41 part20]$ echo "beet" |gawk --re-interval '/be{1,2}t/{print $0}'
beet
[ttxie@41 part20]$ echo "beeeet" |gawk --re-interval '/be{1,2}t/{print $0}'
  1. 也適用于字符組
[ttxie@41 part20]$ echo "bt" |gawk --re-interval '/b[ae]{1,2}t/{print $0}'
[ttxie@41 part20]$ echo "bat" |gawk --re-interval '/b[ae]{1,2}t/{print $0}'
bat
[ttxie@41 part20]$ echo "bet" |gawk --re-interval '/b[ae]{1,2}t/{print $0}'
bet
[ttxie@41 part20]$ echo "baet" |gawk --re-interval '/b[ae]{1,2}t/{print $0}'
baet
[ttxie@41 part20]$ echo "baat" |gawk --re-interval '/b[ae]{1,2}t/{print $0}'
baat
[ttxie@41 part20]$ echo "baaeet" |gawk --re-interval '/b[ae]{1,2}t/{print $0}'
[ttxie@41 part20]$ echo "beaest" |gawk --re-interval '/b[ae]{1,2}t/{print $0}'

如果a或e單獨(dú)持續(xù)1次或2次,模式匹配;ae組合出現(xiàn)1次模式匹配;否則,模式匹配失敗;

20.3.4 管道符號(hào)|

管道符號(hào)允許你在檢查數(shù)據(jù)流時(shí),用邏輯or方式指定正則表達(dá)式引擎要用的兩個(gè)或多個(gè)模式。

格式如下:expr1|expr2|……

[ttxie@41 part20]$ echo "The dog is asleep" |gawk '/cat|dog/{print $0}'
The dog is asleep
[ttxie@41 part20]$ echo "The dog is asleep" |gawk '/cat|[dp]og/{print $0}'
The dog is asleep

20.3.5 表達(dá)式分組()

正則表達(dá)式模式也可以用圓括號(hào)進(jìn)行分組。

  1. 當(dāng)你將正則表達(dá)式模式分組時(shí),該組會(huì)被視為一個(gè)標(biāo)準(zhǔn)字符??梢韵駥?duì)普通字符一樣給該組使用特殊字符。
    ?是匹配0次或1次。
[ttxie@41 part20]$ echo "Sat" |gawk '/Sat(urday)?/{print $0}'
Sat
[ttxie@41 part20]$ echo "Saturday" |gawk '/Sat(urday)?/{print $0}'
Saturday

相當(dāng)于把urday當(dāng)做一個(gè)整體了, /SatF?/ 跟這個(gè)類似,F(xiàn)出現(xiàn)0次或1次。

  1. ()需要轉(zhuǎn)義的用法:
[ttxie@41 part20]$ echo "Sat(urday)" | gawk '/Sat\(urday\)/ {print $0}'
Sat(urday)
[ttxie@41 part20]$ echo "Saturday" | gawk '/Sat\(urday\)/ {print $0}'

還可以將分組和管道符號(hào)一起使用來創(chuàng)建可能的模式匹配組:

[ttxie@41 part20]$ echo "cat" | gawk '/(b|c)a(b|t)/ {print $0}'
cat
[ttxie@41 part20]$ echo "cat" | gawk '/[b|c]a[b|t]/ {print $0}'
cat

這樣相當(dāng)于匹配 bab bat cab cat 四種。

20.4 正則表達(dá)式實(shí)戰(zhàn)

20.4.1 目錄文件計(jì)數(shù)

這個(gè)例子用于對(duì)PATH環(huán)境變量中各個(gè)目錄里的可執(zhí)行文件進(jìn)行計(jì)數(shù):

[ttxie@41 part20]$ cat countfiles.sh
#!/bin/bash
mypath=$(echo $PATH | sed 's/:/ /g')

count=0
for dir in $mypath
do
   check=$(ls $dir)
   for item in $check
   do
       count=$[ count + 1 ]
   done
   echo "$dir - $count"
   count=0
done
[ttxie@41 part20]$ bash countfiles.sh 
/home/ttxie/miniconda2/bin - 0
/usr/local/bin - 0
/usr/bin - 1679
/usr/local/sbin - 0
/usr/sbin - 644
/home/ttxie/.local/bin - 0
/home/ttxie/bin - 0

20.4.2 驗(yàn)證電話號(hào)碼

美國(guó)的號(hào)碼格式如下:

(223)456-7890
(223) 456-7890
223-456-7890
223.456.7890
  • 最開始可能有(可以寫成: ^\(?
  • 三位區(qū)號(hào)(第一位必須大于2):[2-9][0-9]{2}
  • 然后又是括號(hào):\)?
  • 然后是分隔符(無空格,有空格,點(diǎn),減號(hào)):(| |-|\.) 第一個(gè)|緊跟在(后面用來匹配沒有空格的情況。
  • 然后是3位數(shù)字:[0-9]{3}
  • 又是分隔符:( |-|.)
  • 最后四位數(shù)字:[0-9]{4}
  • 連起來就是:^\(?[2-9][0-9]{2}\)?(| |-|\.)[0-9]{3}( |-|\.)[0-9]{4}$
[ttxie@41 part20]$ cat isPhone.sh 
#!/bin/bash

gawk --re-interval '/^\(?[2-9][0-9]{2}\)?(| |-|\.)[0-9]{3}( |-|\.)[0-9]{4}$/{print $0}'
[ttxie@41 part20]$ echo "317-555-1234" |bash isPhone.sh 
317-555-1234
[ttxie@41 part20]$ echo "0317-555-1234" |bash isPhone.sh 

還可以將整個(gè)文件重定向到腳本:

phonelist里面存放著一行一行的數(shù)據(jù):

$cat phonelist | ./isphone

這個(gè)例子其實(shí)也不是特別合理,比如下面幾種情況也算是合法號(hào)碼:

(235.561-4430

  1. 451.4651

總之還是有待優(yōu)化。

20.4.3 解析郵件地址

郵件地址的形式如下:

username@hostname

  1. username值可用字母數(shù)字字符以及以下特殊字符:
  • 點(diǎn)號(hào)
  • 單破折線
  • 加號(hào)
  • 下劃線
  1. hostname部分由一個(gè)或多個(gè)域名和一個(gè)服務(wù)器名組成。只允許字母數(shù)字字符以及下面的特殊字符:比如(xiaochongyong@amwell-haha.com
  • 點(diǎn)號(hào)
  • 下劃線
  1. username@相當(dāng)于:^([a-zA-Z0-9_\-\.\+]+)@

注意: [] 里面是字符組,相當(dāng)于之前的[xcs]。() 里面是表達(dá)式分組

  1. hostname相當(dāng)于:([a-zA-Z0-9_\-\.\]+)

  2. 后面還要接頂級(jí)域名。.com .cn .org 等

只能是字母字符,必須不少于兩個(gè)字符,長(zhǎng)度不超多5個(gè)字符:\.([a-zA-Z]{2,5})$

  1. 連起來就是:

^([a-zA-Z0-9_\.\-\+]+)@([a-zA-Z0-9_\.\-]+)\.([a-zA-Z]{2,5})$

[ttxie@41 part20]$ cat isemail.sh 
#!/bin/bash
gawk --re-interval '/^([a-zA-Z0-9_\.\-\+]+)\@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$/ {print $0}'
[ttxie@41 part20]$ echo "222x2iaochongyong@s1amwell-elec.com" | bash isemail.sh 
222x2iaochongyong@s1amwell-elec.com
[ttxie@41 part20]$ echo "222x2iaocho%ngyong@s1amwell-elec.com" | bash isemail.sh
[ttxie@41 part20]$ echo "222x2iaocho%ngyong+@s1amwell-elec.com" |bash isemail.sh

20.5 小結(jié)

正則表達(dá)式定義了用來過濾數(shù)據(jù)流中文本的模式模板。

模式由標(biāo)準(zhǔn)文本字符和特殊字符的組成。

正則表達(dá)式引擎用特殊字符來匹配一系列單個(gè)或多個(gè)字符,這類似于其他應(yīng)用程序中通配符的工作方式

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容