1. 程式人生 > 其它 >shell指令碼指令碼中正則表示式和sed的使用

shell指令碼指令碼中正則表示式和sed的使用

技術標籤:字串linux正則表示式shell

**

1、正則表示式的使用

**
1.1、概念:
正則表示式(或稱Regular Expression,簡稱RE),是用於描述字元排列和匹配模式的一種語法規則。它主要用於字串的分割,匹配、査找及替換操作。即正則表示式是一種文字模式,該模式描述在搜尋文字時要匹配的一個或多個字串。
在這裡插入圖片描述

這種枯燥的概念難理解,其實,正則表示式是用來匹配檔案中的字串的方法。它會先把整個文字分成一行一行的字串,然後從每行字串中搜索是否有符合正則表示式規則的字串,如果有則匹配成功,如果沒有則匹配失敗。
注:正則表示式和萬用字元的區別(正則表示式用來在檔案中匹配符合條件的字串,萬用字元用來匹配符合條件的檔名)。其實這種區別只在 Shell 中適用,因為用來在檔案中搜索字串的命令,如 grep、awk、sed、vi 等可以支援正則表示式,而在系統中搜索檔案的命令,如 ls、find、cp 等不支援正則表示式,所以只能使用 Shell 自己的萬用字元來進行匹配了。

在正則表示式中,我們把用於匹配的特殊符號又稱作元字元。在shell中,元字 符又分為基礎元字元(BRE)和擴充套件元字元(ERE)。
基礎元字元

                         基礎元字元

元字元的作 用
*匹配前面的一個字元或子表示式0次或任意多次。如:ahello匹配所有0個或多個a後,緊跟hello的行。即hello前面可以有任意多個a。
. 匹配除換行符和回車符(“\n”和"\r")外的任意一個字元。例如:l…e匹配包含一個l,後跟兩個任意字元,然後跟一個e的行。
.
表示匹配任意長度字串
^ 匹配行首。例如,^hello 會匹配以 hello 開頭的行。

$ 匹配行尾。例如,hello$ 會匹配以 hello 結尾的行
^$ 匹配空行
[] 匹配中括號中指定的任意一個字元,而且只匹配一個字元。例如[aoeiu]匹配任意一個母音字母, [0-9] 匹配任意一位數字,[a-z][0-9] 匹配由小寫字母和一位數字構成的兩位字元,[a-zA-Z] 匹配任意一位英文字母

[^] 匹配除中括號中的字元以外的任意一個字元。例如,[^0-9] 匹配任意一位非數字字元,[^a-z] 匹配任意一位非小寫字母,
注意:可以用^標記做[]內的字首,表示除[]內的字元以外的任意一個字元。比如:搜尋oo前沒有g的字串的行,應用 ‘[^g]oo’ 作搜尋字串。符號如果出現在[]的起始位置表示否定,但是在[]的其他位置是普通字元。[

ab^c] 匹配除了a、b、^、c以外的任意單個字元。
\ 轉義符,用於取消特殊符號的含義,使該特殊字元成為普通字元。例如:^.[0-9][0-9]表示匹配以一個句點和兩個數字開始。
{n} 表示其前面的字元出現 n 次。例如,[0-9]{4} 匹配4位數字,1[35-9][0-9]{9} 匹配手機號碼。
{n,} 表示其前面的字元出現不少於 n 次。例如,[0-9]{2,} 匹配兩位及以上的數字
{n,m} 表示其前面的字元至少出現 n 次,最多出現 m 次。例如,[a-z]{6,8} 匹配 6〜8 位的小寫字母
\<
\> 匹配詞(word)的開始(<)和結束(>)。例如正則表示式<the>能夠匹配字串"for the wise"中的"the",但是不能匹配字串"otherwise"中的"the"。注意:這個元字元不是所有的軟體都支援的。
擴充套件正則表示式
熟悉正則表示式的人應該很疑惑,在正則表示式中應該還可以支援一些元字元,比如"+"、"?"、"|"、"()"。
其實 Linux 是支援這些元字元的,只是 grep 命令預設不支援而已,因為grep把這些擴充套件元字元看成是普通符號,如果要想支援這些元字元,則可以使用 egrep 或 grep -E 命令。所以我們又把這些元字元稱作擴充套件元字元。
如果査詢 egrep 命令的幫助,對 egrep 的說明就是和 grep -E 一樣的命令,
Shell 中支援的擴充套件元字元。
擴充套件元字元 描述

  • 匹配前面的一個字元或子表示式1次或任意多次。
    如“go+gle”會匹配“gogle” “google”或“gooogle”。當然,如果“o”有更多個,則也能匹配。
    egrep “go+gle” filename或grep -E “go+gel” filename
    ? 匹配前面的一個字元或子表示式零次或一次。例如:如 “colou?r” 可以匹配 “colour” 或 “color”
    | 表示或。如“was|his”既會匹配包含“was”的行,或匹配包含“his”的行
    () 將括號裡的內容看成是一個整體。可以理解為由多個單個字元組成的大字元。
    如“(dog)+”會匹配“dog” “dogdog” “dogdogdog”等,因為被()包含的字元會被當成一個整體。但 “hello(world|earth)” 會匹配 “hello world” 及 “hello earth”
    注:"+"、"?"、"|"、"()"、"{}“等擴充套件元字元egrep命令或grep -E是支援的。grep命令在不加-E選項的情況下可以按如下格式寫:”+"、"?"、"|"、"()"、"{}"
    正則表示式範例
    下面舉例來說明這些元字元的作用。我們已經學習過的 grep 命令支援正則表示式,所以下面的練習都需要利用 grep 命令來演示。在使用 grep 命令開始練習之前,建議大家在 ~/.bashrc 檔案中建立這個別名,如下:

[[email protected] ~]# vi /root/.bashrc
alias grep=‘grep --color=auto’
執行source命令使修改生效
[[email protected] ~]#source /root/.bashrc
這樣,grep 命令所匹配的字元都會使用顏色提示,更加容易理解正則表示式所具體匹配的字串。
練習檔案建立:
既然正則表示式是用來在檔案中匹配字串的,那麼我們必須建立一個測試用的檔案,才可以進行後續的實驗。檔案如下:
[[email protected] ~]# cat test_rule.txt
Mr. Li Ming said:
he was the most honest man in LampBrother.
123despise him.

But since Mr. shen Chao came,
he never saaaid those words.
5555nice!

because,actuaaaally,
Mr. Shen Chao is the most honest man
Later,Mr. Li ming soid his hot body.
1、"":前一個字元匹配0次或任意多次
注意,"
“和萬用字元中的”"含義不同,它代表前一個字元重複 0 次或任意多次。比如,"a"並不是匹配"a"後面的任意字元,而是可以匹配所有內容,包括空白行。我們試試:

為什麼會這樣呢? "a*"代表匹配 0 個 a 或無數個 a,如果是匹配 0 個 a,也就是每個字元都會匹配,所以會匹配所有內容,包括空白行。所以"a*“這樣的正則表示式是沒有任何意義的。
如果這樣寫正則表示式"aa*”,則代表這行字串一定要有一個 a,但是後面有沒有 a 都可以。也就是說,會匹配至少包含一個 a 的行。

注:“”修飾的它前面的一個字元a,而不是字串aa
如果正則表示式是"aaa
",則會匹配最少包含兩個連續 a 的字串。
2、".":匹配除換行符和回車符外的任意一個字元
正則表示式"."只能匹配一個字元,這個字元可以是任意字元。舉個例子:

#"s…d"會匹配在s和d這兩個字母之間一定有兩個字元的單詞
如果我想匹配在 s 和 d 字母之間有任意字元的單詞, 那麼該怎麼寫呢?“sd"這個正則表示式肯定是不行的,因為它會匹配包含 d 字元的行,s可以匹配任何字元。正確的寫法應該是"s.*d”。例如:

3、"^":匹配行首,"$":匹配行尾
"":代表匹配行首,比如"M"會匹配以大寫"M"開頭的行。

" " 代 表 匹 配 行 尾 , 比 如 " n "代表匹配行尾,比如"n ""n"會匹配以小寫"n"結尾的行。

注意,如果文件是在 Windows 中寫入的,那麼"M " 是 不 能 正 確 執 行 的 , 因 為 在 W i n d o w s 中 換 行 符 是 " M "是不能正確執行的,因為在 Windows 中換行符是"^M "Windows"M",而在 Linux 中換行符是"KaTeX parse error: Expected group after '^' at position 114: … 這個 RPM 包即可。 而"^̲"則會匹配空白行。

如果不加"-n"選項,空白行是沒有任何顯示的;加入了"-n"能看到空白行的行號。
4、"[]":匹配中括號中指定的任意一個字元,且只匹配一個字元
"[]"會匹配中括號中指定的任意一個字元,注意只能匹配一個字元。比如 [ao] 要麼匹配 a 字元,要麼匹配一個 o 字元。

而"[0-9]"會匹配任意一個數字,例如:

#列出包含有數字的行
而"[A-Z]“則會匹配任意一個大寫字母,如果正則表示式是”1",則代表匹配以小寫字母開頭的行。
“[^]”:匹配除中括號的字元以外的任意一個字元
這裡需要注意,如果"^“在 [] 外,則代表的是行首;如果在 [] 內,則代表的是取反。比如”[a-z]“會匹配以小寫字母開頭的行,而”[^a-z]"會匹配不以小寫字母開頭的行。

而"[a-zA-Z]“會匹配不以字母開頭的行。
5、:”":轉義符
轉義符會取消特殊符號的含義。如果想要匹配使用".“結尾的行,那麼正則表示式是”.KaTeX parse error: Can't use function '\.' in math mode at position 49: …所以需要在前面加入轉義符,如"\̲.̲"。

6、"{n}":表示其前面的字元出現 n 次
"{n}"中的 n 代表數字,這個正則表示式會匹配前一個字元出現 n 次的字串,比如"zo{3}m"只能匹配"zooom"這個字串。例如,"a{3}"就會匹配 a 字母連續出現 3 次的字串。

如果正則表示式是"[0-9]{3}",則會匹配包含三個連續數字的字串。

雖然"5555"有四個連續的數字,但是包含三個連續的數字,所以也是可以列出的。
只匹配以連續三個數字開頭,後面緊跟小寫字母的行,

"{n,}"表示其前面的字元出現不少於 n 次。
#匹配最少以連續三個數字開頭的行

"{n,m}"表示其前面的字元至少出現n次,最多出現m次。

#匹配在字母s和字母i之間最少有一個a、最多有3個a的字串
例1:顯示sshd_config檔案中有效的配置項
[[email protected] ~]# egrep -v “#|KaTeX parse error: Expected 'EOF', got '#' at position 45: …[email protected] ~]#̲ grep -E -v …” /etc/ssh/sshd_config
例2:過濾IP地址
在/tmp/hosts檔案記錄著相關ip地址資訊,內容如下:

利用正則表示式過濾出ip地址
答案有很多種,提示:
提示 :IP地址從0-255之間,要把0-255拆分成0-99,100-199,200-249,250-255,然後在分別過濾出4段就出來了。正則表示式書寫如下所示:

例3:過濾手機號
在/tmp/phone_number檔案中存放著電話碼資訊,內容如下
#cat /tmp/phone_number.txt
13421391020
13521591629
1354243919046
15421391020
16421391020
42456789898
198384732947
利用正則表示式過濾出正確的電話號碼

sed命令
2.1、概念
sed全名叫stream editor,流編輯器。用無互動式的方式來編輯文字。
我們知道,vim/vi 採用的是互動式文字編輯模式,你可以用鍵盤命令來互動性地插入、刪除或替換資料中的文字。但本節要講的 sed 命令不同,它採用的是流編輯模式,最明顯的特點是,在sed 處理資料之前,需要預先提供一組規則,sed會按照此規則來編輯資料,實現無互動式編輯資料。
sed也是支援正則表示式的,如果要使用擴充套件正則加引數-r
sed的執行過程:
sed編輯器逐行處理檔案(或輸入),並將結果傳送到螢幕。
具體過程如下:
1、首先sed把當前正在處理的行儲存在一個臨時快取區中(也稱為模式空間)。
2、然後處理臨時緩衝區中的行,完成後把該行傳送到螢幕上。
3、sed每處理完一行就將其從臨時緩衝區刪除,然後將下一行讀入,進行處理和顯示。
4、處理完輸入檔案的最後一行後,sed便結束執行。
大家需要注意,sed 預設不會直接修改原始檔資料,而是會將資料複製到緩衝區中,修改也僅限於緩衝區中的資料,並把修改結果只顯示到螢幕上,除非使用"-i"選項才會直接修改檔案。
sed使用
sed 命令的基本格式如下:
[[email protected] ~]# sed [選項] ‘[動作指令]’ filename
選項
選項:
-n 預設情況下,sed 會在動作指令執行完畢後,自動輸出處理後的內容,而該選項會遮蔽預設輸出。
-e 執行多個sed指令
-i 此選項會直接修改原始檔,要慎用,修改前建議先備份原始檔。
-i.bak 編輯原始檔的同時創造.bak的備份
-r 使用擴充套件的正則表示式
動作指令:
p 列印 ,輸出指定的行
S 替換,替換指定字串
d 刪除,刪除行
a 增加行,在當前行下面插入檔案
i 增加行,在當前行上面插入檔案
c 把選定的行改為新的指定的文字
r 讀取檔案,即用於將一個獨立檔案的資料插入到當前資料流的指定位置
w 另存為
注意:動作指令要是用單引號或雙引號括起來。
sed常用動作指令:
列印(p指令)
我們先準備一個測試檔案:
[[email protected] ~]# cat test.txt
my cat’s name is betty
This is your dog
my dog’s name is frank
This is your fish
my fish’s name is george
This is your goat
my goat’s name is adam

P指令表示搜尋符號條件的行或指定範圍的行,並輸出該行的內容。
例1:搜尋包含betty關鍵字的行並顯示
注意:搜尋條件要使用“/…/”括起來。

注:p指令是預設輸出所有行,找到betty的行重複列印。
如果我想指定輸出某行資料,就需要"-n"選項(禁止預設輸出,只打印找到betty的行)。

可以看到,用 -n 選項和 p 命令配合使用,我們可以禁止輸出其他行,只打印包含匹配文字模式的行。

例2:如果想檢視一下test.txt檔案的第二行,只需要指定行號即可

如果需要對同一檔案或行作多次指令動作,可以使用 “-e” 選項

替換(s指令)
我們先準備一個測試檔案,檔案內容如下:
[[email protected] ~]# cat test1.txt
it is hello too old to learn
it is hello too hello to learn never
when the cat is away,the mice will play
no cross,no crown

sed的s指令可以以行為單位進行部分資料的搜尋並替換。基本上sed的搜尋與替代的與vi相當的類似。
格式:sed ‘s/要被替換的字串/新的字串/g’
例如:[[email protected] ~]# sed ‘s/hello/never/g’ test1.txt
s "替換"命令
/…/…/ 分割符 (Delimiter)
hello 搜尋字串(要被替換的字串)
never 替換字串(新的字串)
注:其實 , 分割符 “/” 可以用別的符號代替 , 比如 ","或 "|“或”@"等 。
如:sed ‘s//usr/local/bin//usr/bin/’ filename
等價於 sed ‘[email protected]/usr/local/[email protected]/usr/[email protected]’ filename
顯然 , 此時用 “@” 作分割符比 “/” 好得多
注:匹配test1.txt檔案中每一行的第一個hello替換為never,使用字尾/g標記會替換每一行中的所有匹配。
[[email protected] ~]# sed ‘s/hello/never/’ test1.txt //只替換每行出現的第一個詞
it is never too old to learn
it is never too hello to learn never
when the cat is away,the mice will play
no cross,no crown
[[email protected] ~]# sed ‘s/hello/never/2’ test1.txt //只替換每行出現的第二個詞
it is hello too old to learn
it is hello too never to learn never
when the cat is away,the mice will play
no cross,no crown
[[email protected] ~]# sed ‘s/hello/never/g’ test1.txt //全域性替換
it is never too old to learn
it is never too never to learn never
when the cat is away,the mice will play
no cross,no crown
說明:/表示定界符,定界符是可以自定義的
[[email protected] ~]# head -5 /etc/passwd > mima
[[email protected] ~]# head -2 mima
root❌0:0:root:/root:/bin/bash
bin❌1:1:bin:/bin:/sbin/nologin
[[email protected] ~]# head -2 mima |sed ‘[email protected]/sbin/[email protected]/bin/[email protected]
root❌0:0:root:/root:/bin/bash
bin❌1:1:bin:/bin:/bin/bash
[[email protected] ~]# head -2 mima |sed ‘s/sbin/nologin/bin/bash/’
root❌0:0:root:/root:/bin/bash
bin❌1:1:bin:/bin:/bin/bash
按行查詢替換
用數字表示行範圍
例1:單選替換
[[email protected] ~]# cat test2.txt
it is never too old to learn
it is never too never to learn never
when the cat is away,the mice will play
no cross,no crown
[[email protected] ~]# sed ‘2s/never/dog/’ test2.txt
it is never too old to learn
it is dog too never to learn never
when the cat is away,the mice will play
no cross,no crown
例2:多行替換
[[email protected] ~]# cat test3.txt
Doing is better than saying
Doing is better than saying
Doing is better than saying
Doing is better than saying
Doing is better than saying
[[email protected] ~]# sed ‘3,$s/better/saying/’ test3.txt
Doing is better than saying
Doing is better than saying
Doing is saying than saying
Doing is saying than saying
Doing is saying than saying
如果想把某行註釋掉,讓它不再生效,則可以這樣做:
例如:將test2.txt檔案的第4行註釋掉

如果想把某個字串(如never)替換為空,則可以這樣做:

取得ens33網絡卡IP地址:

例3:多個命令使用
[[email protected] ~]# cat test2.txt
it is never too old to learn
it is never too never to learn never
when the cat is away,the mice will play
no cross,no crown
[[email protected] ~]# sed ‘s/cat/dog/’ test2.txt
it is never too old to learn
it is never too never to learn never
when the dog is away,the mice will play
no cross,no crown
[[email protected] ~]# sed ‘s/never/@@/’ test2.txt
it is @@ too old to learn
it is @@ too never to learn never
when the cat is away,the mice will play
no cross,no crown
[[email protected] ~]# sed ‘s/never/@@/;s/cat/dog/’ test2.txt //第一種用“;”分隔多個sed操作指令。
it is @@ too old to learn
it is @@ too never to learn never
when the dog is away,the mice will play
no cross,no crown
[[email protected] ~]# sed -e ‘s/never/@@/’ -e ‘s/cat/dog/’ test2.txt //第二種用-e選項
刪除行(d指令)
如果需要刪除文字中的特定行,可以用d指令,它會刪除指定行中的所有內容。但使用該命令時要特別小心,如果你忘記指定具體行的話,檔案中的所有內容都會被刪除。
[[email protected] ~]# cat test1.txt
it is hello too old to learn
it is hello too hello to learn never
when the cat is away,the mice will play
no cross,no crown

什麼也不輸出,證明成了空檔案.
[[email protected] ~]# sed ‘2d’ test1.txt //指定行號刪除
it is hello too old to learn
when the cat is away,the mice will play
no cross,no crown
通過特定行區間指定,比如刪除 test1.txt 檔案內容中的第 2、3行:

通過特殊的檔案結尾字元,比如刪除test1.txt檔案內容中第 3 行開始的所有的內容:

[[email protected] ~]# sed ‘/cat/d’ test1.txt //根據匹配的內容去刪除
it is hello too old to learn
it is hello too hello to learn never
no cross,no crown
注:在此強調,在預設情況下 sed 並不會修改原始檔案,這裡被刪除的行只是從 sed 的輸出中消失了,原始檔案沒做任何改變。
新增行(i指令和a指令)
命令i(insert插入),在指定行前面插入一行
命令a(append附加),在指定行後面新增一行
它們的基本格式完全相同,如下所示: a(或 i)\新文字內容
例1:插入內容
例1:插入
將一個新行插入到檔案的第三行前,執行命令如下:

例2:追加
將一個新行附加到檔案的第三行後,執行命令如下:

例3:在檔案尾部新增新行內容

在第2行到第4行後分別新增一新行(即在2,3,4行後分別插入)

例4:如果想追加或插入多行資料,則除最後一行外,每行的末尾都要加入""代表資料未完結。

修改行(c指令)
c 命令表示將指定行中的所有內容,替換成該選項後面的字串。
"c"動作是進行整行替換的,如果僅僅想替換行中的部分資料,就要使用"s"動作了。
指令c的格式:c\用於替換的新文字
[[email protected] ~]# cat test1.txt
it is hello too old to learn
it is hello too hello to learn never
when the cat is away,the mice will play
no cross,no crown
[[email protected] ~]# sed ‘2,$c\hello world’ test1.txt
it is hello too old to learn
hello world
[[email protected] ~]# sed ‘2c\hello world’ test1.txt
it is hello too old to learn
hello world
when the cat is away,the mice will play
no cross,no crown
[[email protected] ~]# sed ‘/when/c\hello world’ test1.txt
it is hello too old to learn
it is hello too hello to learn never
hello world
no cross,no crown
#sed ‘/SELINUX=enforcing/c\SELINUX=disabled’ /etc/selinux/config

對檔案的儲存和讀取
例1:讀取
r 命令用於將一個獨立檔案的資料插入到當前檔案的指定位置,該命令的基本格式為:[address]r filename
sed 命令會將 filename 檔案中的內容插入到 address 指定行的後面,比如說:
[[email protected] ~]# cat test1.txt
it is hello too old to learn
it is hello too hello to learn never
when the cat is away,the mice will play
no cross,no crown
[[email protected] ~]# sed ‘3r /etc/hosts’ test1.txt

如果你想將指定檔案中的資料插入到當前檔案的末尾,可以使用 $符,例如:

/etc/hosts裡的內容被讀進來,顯示在與test1.txt匹配的行後面,如果匹配多行,則/etc/hosts的內容將顯示在所有匹配行的下面:

例2:寫入
將test1.txt檔案修改的行寫入test11.txt檔案中。

在test1.txt中所有包含hello的行都被寫入test12.txt裡:

對原檔案直接修改
-i選項:此選項會直接修改原始檔
[[email protected] ~]# sed -i ‘s/cat/dog/’ test1.txt
[[email protected] ~]# cat test1.txt
it is hello too old to learn
it is hello too hello to learn never
when the dog is away,the mice will play
no cross,no crown
[[email protected] ~]# sed -i.bak ‘s/dog/cat/’ test1.txt
[[email protected] ~]# cat 1.txt
it is hello too old to learn
it is hello too hello to learn never
when the cat is away,the mice will play
no cross,no crown
[[email protected] ~]# cat test1.txt.bak
it is hello too old to learn
it is hello too hello to learn never
when the dog is away,the mice will play
no cross,no crown
去除行首數字
[[email protected] ~]# cat test2.txt
1234 it is never too old to learn
999 it is never too never to learn never
when the cat is away,the mice will play
353463463 no cross,no crown
ni hao xx nisdfsdf
[[email protected] ~]# sed -r ‘s/2+//g’ test2.txt
it is never too old to learn
it is never too never to learn never
when the cat is away,the mice will play
no cross,no crown
ni hao xx nisdfsdf


  1. a-z ↩︎

  2. 0-9 ↩︎