sed正則表達式

 sed正則表達式 2009-11-16 18:30:43

分類: LINUX


1 正則表達式簡介

正則表達式(Regular Expression)是一種描述文本(或字符串)模式的工具。正則表達式常用於查找文本的場合。想想一下我們日常生活中的例子,假如你想從電話本里找一個聯繫人的電話,而你又想不起聯繫人名字的準確拼法,你可以把電話本從頭到尾翻一遍,如果電話本里聯繫人少的話也許並不是什麼麻煩事,但想象一下如果這是記錄着公司所有員工的電話本,你面臨的工作便複雜得多了,有可能是從幾百甚至幾千條記錄中尋找你想要的那條電話記錄!而利用正則表達式則爲解決這一問題提供了一種簡便有效的方法。
正則表達式是一種描述具有某些共同特徵的文本的文本,在不同的環境裏有不同的格式,但其大致格式是相似的。本文將介紹Unix系統下的正則表達式。
其實,也許在你不清楚正則表達式是什麼的時候你已經開始使用它了。想想Linux裏一個最常用的命令:
ls *.tar
在這個命令裏,*.tar 便是一個正則表達式,它是一個描述了這樣的字符串的字符串:以任意數量的任意字符開頭(*),後面緊接一個句點(.),然後再跟一個tar(這是在Unix Shell裏表示的意思,在其他場合有不同的意思,後文將介紹)。星號(*)我們在DOS裏常稱之爲“通配符(wildcard)”,而在正則表達式裏我們稱之爲“元字符(metacharacter)”。下一章節我們將介紹正則表達式裏常用的元字符。

2 正則表達式元字符和格式

在上一節裏我們用一個例子介紹了什麼是正則表達式的“元字符”。其實,元字符是一個或一組代替個或多個字符的字符。聽起來有點拗口,但舉一個例子也許你就明白了:元字符*用來匹配一個或多個的前一字符;而元字符 用來匹配一個任意的一個字符。正則表達式也可以不使用任何的元字符,一個簡單的字符串 /piano/ (在Unix里正則表達式通常用一對斜線作爲分隔符,後文在“正則表達式格式”部分中有介紹)也是一個正則表達式,只不過是準確匹配罷了。

下面列出了常用的正則表達式元字符,下一節將以實例的形式逐個介紹這些元字符的用法。需要注意的是,這些元字符並不是在所有的Unix (Linux) 工具中都可以用,至於某個應用程序支持哪些元字符,可以參照該工具的用戶手冊。
 
2.1 正則表達式元字符
 

元字符

功能
示例
匹配結果
^
行首定位符
/^supinfo/
匹配所有以supinfo開頭的行
 
$
行尾定位符
/supinfo$/
匹配所有以supinfo結尾的行
 
\<
詞首定位符
/\<supinfo/
匹配出現以supinfo爲開頭的詞的行
 
\>
詞尾定位符
/supinfo\>/
匹配出現以supinfo爲結尾的詞的行
 
.
匹配一個字符
/sufo/
包含su,後面緊跟三個任意字符,然後緊跟着fo的行
 
*
匹配0個或多個前一字符
/_*supinfo/
supinfo前有0個或多個下劃線的行
 
[]
匹配一組字符裏的任意字符
 
/[Ss]pinfo/
包含Supinfosupinfo的行
[x-y]
匹配指定範圍內的字符
/[A-Z0-9]supinfo/
supinfo之前有一個AZ09的字符
 
[^ ]
匹配不在指定範圍內的字符
/[^A-Z0-9]supinfo/
supinfo之前有一個既不是AZ又不是09的字符
 
x\{m\}
x\{m,\}
x\{mn\}
根據字符x出現的次數匹配:
m次;大於等於m次;大於等於m次但小於等於n
 
/s\{2,5\}/
匹配有25個連續出現的s的行
\
轉義元字符
/supinfo\. /
匹配包含supinfo,然後後面緊跟一個句點的行(沒有 的時候是匹配一個字符)
 
\(\)
創建一個字符標籤
/(SUPINFO):use\1NE/
括號中的字符被保存在標號爲1的標籤裏,以後可以用\1來引用。標籤編號從左到右依次爲123……最多可以有9個標籤。這個例子查找的是SUPINFO:後面跟着一個 use SUPINFONE的字符串
 
還有一點問題需要在這裏提出,正則表達式有很多版本——至少存在兩個版本:基本正則表達式和擴展正則表達式。在基本正則表達式中,原字符 |  ? 是不允許使用的。並且在使用含有圓括號和花括號的正則表達式時,圓括號和花括號都要用反斜線轉義。在書寫正則表達式時,比較好的做法是先寫出整個正則表達式,然後再用反斜線將需要轉義的字符轉義。
正則表達式的格式
Unix 中,正則表達式是被包含在一對斜線中的,斜線之間包含要匹配的字符(模式)。下面是幾個例子:
/piano/ /[Pp]iano/            /*pinfo/               /s\{2,5\}oho/

3 正則表達式實例解析

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

一個最簡單的例子便是 /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

 

詞尾定位符 \>

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

/\/

匹配以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的字符串。在這個例子中,BobbyBippy都會被匹配。

 

匹配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/

方括號匹配一組字符中的一個,這個正則表達式查找的是第一個字母是bw,後面緊跟着all的字符串,因此在這個例子中,wallball都會被匹配。

 

匹配指定範圍內的字符 [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碼介於az的字符(小寫字母),第三個字母是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,這個特殊字符既不是小寫字母又不是大寫字母,也不是09的數字,比如它可以是一個標點符號或空格。

 

根據字符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編輯器將查找單詞Occurenceoccurence,如果找到,就把圓括號中的內容加上標籤(Occuroccur被加上標籤),因爲這是第一個被標記的模式,所以被稱爲標籤1。這個模式被保存在稱爲寄存器1的內存寄存器中。在第二個正則表達式中用\1引用寄存器1中的內容,\1被替換爲寄存器中的內容,後面緊跟一個rence,於是,拼錯的Occurenceoccurence被改正爲正確的Occurrenceoccurrence

 

3.7            原字符組合使用的例子

 

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.

/\/

匹配以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個或多個重複的bp,最後跟一個y,並且這個字符串位於行的末尾。


4 sed原理及sed命令格式

4.1            Sed工作原理

 

sed是一個非交互式的流編輯器。所謂非交互式,是指使用sed只能在命令行下輸入編輯命令來編輯文本,然後在屏幕上查看輸出;而所謂流編輯器,是指sed每次只從文件(或輸入)讀入一行,然後對該行進行指定的處理,並將結果輸出到屏幕(除非取消了屏幕輸出又沒有顯式地使用打印命令),接着讀入下一行。整個文件像流水一樣被逐行處理然後逐行輸出。

 

下面我們看一下sed的工作過程。

 

sed不是在原輸入上直接進行處理的,而是先將讀入的行放到緩衝區中,對緩衝區裏的內容進行處理,處理完畢後也不會寫回原文件(除非用shell的輸出重定向來保存結果),而是直接輸出到屏幕上。sed運行過程中維護着兩個緩衝區,一個是活動的“模式空間(pattern space)”,另一個是起輔助作用的“暫存緩衝區(holding space)”。一般情況下,每當運行sedsed首先把第一行裝入模式空間,進行處理後輸出到屏幕,然後將第二行裝入模式空間替換掉模式空間裏原來的內容,然後進行處理,以此類推。

插圖

 

一般情況下暫存緩衝區是用不到的,但有特殊的命令可以在模式空間與暫存緩衝區之間交換數據,後文將有介紹。由於sed對文本的所有操作都是在緩衝區裏進行的,所以不會對原文件造成任何破壞。

 

4.2            Sed命令格式

 

sed的命令格式如下:

 

sed [-Options] [‘Commands’] filename

 

其中,Command是一個sed命令,sed命令一定要被包含在一對單引號中,以免被shell解釋,其格式如下:

 

[address-range][sed-command]或

[Pattern-to-match][sed-command]

 

address-range是指要處理的行的範圍,又叫地址範圍;pattern-to-match是一個要匹配的模式,是一個正則表達式,sed-command是一個sed命令,用來對指定的行進行處理。下面是一個簡單的例子:

 

sed –n ‘1,3p’ students

 

這個命令將文件students中的第13行打印到屏幕。注意,地址範圍和sed命令之間沒有空格,如果加入空格,sed也會將其忽略。參數-n用來取消默認輸出。默認情況下,sed每讀入一行到模式空間,無論是否對其進行處理,在讀入下一行之前多要將模式空間中的內容輸出到屏幕上。參數-n可以用來取消這種默認的輸出,只有當用戶用命令p時纔將指定的行輸出到屏幕。如果沒有用參數-n而又對指定行執行了p命令,那麼這些行將會被打印兩次。

 

地址範圍可以是一個數字,這個數字代表了一個行號;也可以是一個用逗號分隔的兩個數字表示的範圍(包括這兩行)。範圍可以是數字,正則表達式,或是兩者的組合。

 

pattern-to-match是一個要匹配的模式,sed將會對所有匹配的行執行sed-command。其實,這裏的pattern-to-match也可以看作是一個地址,這個地址是所有與指定模式匹配的行的行號。因此sed的格式可以歸納爲一種:

 

sed [-Options] ‘[address-range][sed-command]’ filename



5 sed命令與選項

5.1            Sed命令

 

常用的sed命令如下表所列:

命令

功能

a\

在當前行之後插入一行或多行

 

c\

用新文本替換當前行中的文本,並開始新的一輪sed命令的執行

 

d

刪除行

 

i\

在當前行之前插入文本

 

h

將模式空間裏的內容拷貝到暫存緩衝區並替換原來暫存緩衝區的內容

 

H

將模式空間裏的內容追加到暫存緩衝區

 

g

將裏暫存緩衝區的內容拷貝到模式空間並替換原來模式空間的內容

 

G

將暫存緩衝區裏的內容追加到模式空間

 

p

打印模式空間的內容

 

n

讀入下一行到模式空間,並接着從下一條命令開始執行

 

q

直接退出sed,不繼續執行其後的命令

 

r

讀入指定文件的內容

 

w

將行寫入文件

 

!

對所選行以外的行進行處理

 

s/regexp/replacement/flag

replacement替換模式空間由regexp匹配到的內容

 

x

交換模式空間與暫存緩衝區的內容

 

y/source-chars/dest-chars/

source-chars的字符換成對應的的dest-chars中的字符,source-charsdest-chars中的字符個數要相同。source-charsdest-chars中都不能有正則表達式。

 

=

打印當前行的行號,行號是令起一行打印的

 

#

sed腳本文件中領起註釋

替換命令s/regexp/replacement/flag 中的flag

flag

功能

g

進行全局替換。不使用此選項將只對該行匹配到的第一個結果進行替換

p

打印模式空間中的內容(替換之後的內容)

filename

將替換之後的內容寫入文件filename

 

在後面的章節“sed實例解析”中我們將以實例的形式詳細介紹各個命令的用法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章