Unix/Linux - 使用sed的正則表示式



在本章中,我們將詳細討論Unix中使用sed的正則表示式。

正則表示式是一個可以用來描述多個字元序列的字串。許多不同的Unix命令都使用正則表示式,包括edsedawkgrep,以及在有限程度上vi

這裡SED代表stream editor(流編輯器)。這個面向流的編輯器專門用於執行指令碼。因此,您輸入的所有內容都會透過並輸出到STDOUT,它不會更改輸入檔案。

呼叫sed

在開始之前,讓我們確保我們有一個/etc/passwd文字檔案的本地副本,以便與sed一起使用。

如前所述,sed可以透過將資料透過管道傳送到它來呼叫,如下所示:

$ cat /etc/passwd | sed
Usage: sed [OPTION]... {script-other-script} [input-file]...

  -n, --quiet, --silent
                 suppress automatic printing of pattern space
  -e script, --expression = script
...............................

cat命令將/etc/passwd的內容透過管道轉儲到sed,進入sed的模式空間。模式空間是sed用於其操作的內部工作緩衝區。

sed通用語法

以下是sed的通用語法:

/pattern/action

這裡,pattern是正則表示式,action是下表中給出的命令之一。如果省略pattern,則對每一行執行action,如上所示。

圍繞模式的斜槓字元(/)是必需的,因為它們用作分隔符。

序號 範圍和描述
1

p

列印行

2

d

刪除行

3

s/pattern1/pattern2/

將pattern1的第一個出現替換為pattern2

使用sed刪除所有行

我們現在將瞭解如何使用sed刪除所有行。再次呼叫sed;但sed現在應該使用編輯命令刪除行,由單個字母d表示:

$ cat /etc/passwd | sed 'd'
$

而不是透過管道將檔案傳送到sed來呼叫sed,sed可以被指示從檔案讀取資料,如下例所示。

以下命令與前面的示例完全相同,無需cat命令:

$ sed -e 'd' /etc/passwd
$

sed地址

sed還支援地址。地址要麼是檔案中的特定位置,要麼是應該應用特定編輯命令的範圍。當sed遇到沒有地址時,它會在檔案中的每一行上執行其操作。

以下命令為sed命令添加了一個基本地址:

$ cat /etc/passwd | sed '1d' |more
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
$

請注意,數字1是在刪除編輯命令之前新增的。這指示sed對檔案的第一行執行編輯命令。在本例中,sed將刪除/etc/password的第一行並列印檔案的其餘部分。

sed地址範圍

我們現在將瞭解如何使用sed地址範圍。那麼,如果您想從檔案中刪除多行怎麼辦?您可以使用sed指定地址範圍,如下所示:

$ cat /etc/passwd | sed '1, 5d' |more
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
$

上述命令將應用於從1到5的所有行。這將刪除前五行。

嘗試以下地址範圍:

序號 範圍和描述
1

'4,10d'

從第4行到第10行都被刪除

2

'10,4d'

僅刪除第10行,因為sed不反向工作

3

'4,+5d'

這匹配檔案中的第4行,刪除該行,繼續刪除接下來的五行,然後停止刪除並列印其餘部分

4

'2,5!d'

這刪除除第2行到第5行之外的所有內容

5

'1~3d'

這刪除第一行,跳過接下來的三行,然後刪除第四行。sed繼續應用此模式,直到檔案結束。

6

'2~2d'

這告訴sed刪除第二行,跳過下一行,刪除下一行,並重復直到檔案結束

7

'4,10p'

列印從第4行到第10行

8

'4,d'

這會產生語法錯誤

9

',10d'

這也會產生語法錯誤

注意 - 使用p操作時,應使用-n選項避免重複列印行。檢查以下兩個命令之間的區別:

$ cat /etc/passwd | sed -n '1,3p'
Check the above command without -n as follows −
$ cat /etc/passwd | sed '1,3p'

替換命令

替換命令(由s表示)將替換您指定的任何字串與您指定的任何其他字串。

為了用另一個字串替換一個字串,sed需要知道第一個字串在哪裡結束以及替換字串在哪裡開始的資訊。為此,我們繼續使用正斜槓(/)字元將兩個字串括起來。

以下命令將一行上root字串的第一次出現替換為amrood字串。

$ cat /etc/passwd | sed 's/root/amrood/'
amrood:x:0:0:root user:/root:/bin/sh
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
..........................

需要注意的是,sed只替換一行上的第一次出現。如果字串root在一行上出現多次,則只替換第一個匹配項。

為了使sed執行全域性替換,請在命令末尾新增字母g,如下所示:

$ cat /etc/passwd | sed 's/root/amrood/g'
amrood:x:0:0:amrood user:/amrood:/bin/sh
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
...........................

替換標誌

除了g標誌之外,還可以傳遞許多其他有用的標誌,並且您可以一次指定多個標誌。

序號 標誌和描述
1

g

替換所有匹配項,而不僅僅是第一個匹配項

2

NUMBER

僅替換第NUMBER個匹配項

3

p

如果進行了替換,則列印模式空間

4

w FILENAME

如果進行了替換,則將結果寫入FILENAME

5

I或i

不區分大小寫地匹配

6

M或m

除了特殊正則表示式字元^和$的正常行為之外,此標誌還會導致^匹配換行符後的空字串,以及$匹配換行符前的空字串

使用備用字串分隔符

假設您必須對包含正斜槓字元的字串進行替換。在這種情況下,您可以透過在s之後提供指定的字元來指定不同的分隔符。

$ cat /etc/passwd | sed 's:/root:/amrood:g'
amrood:x:0:0:amrood user:/amrood:/bin/sh
daemon:x:1:1:daemon:/usr/sbin:/bin/sh

在上面的示例中,我們使用了:作為分隔符而不是斜槓/,因為我們試圖搜尋/root而不是簡單的root。

用空格替換

使用空替換字串從/etc/passwd檔案中完全刪除root字串:

$ cat /etc/passwd | sed 's/root//g'
:x:0:0::/:/bin/sh
daemon:x:1:1:daemon:/usr/sbin:/bin/sh

地址替換

如果只想在第10行將字串sh替換為字串quiet,可以如下指定:

$ cat /etc/passwd | sed '10s/sh/quiet/g'
root:x:0:0:root user:/root:/bin/sh
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/quiet

同樣,要進行地址範圍替換,您可以執行以下操作:

$ cat /etc/passwd | sed '1,5s/sh/quiet/g'
root:x:0:0:root user:/root:/bin/quiet
daemon:x:1:1:daemon:/usr/sbin:/bin/quiet
bin:x:2:2:bin:/bin:/bin/quiet
sys:x:3:3:sys:/dev:/bin/quiet
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh

從輸出中可以看到,前五行將字串sh更改為quiet,但其餘行保持不變。

匹配命令

您將使用p選項以及-n選項列印所有匹配行,如下所示:

$ cat testing | sed -n '/root/p'
root:x:0:0:root user:/root:/bin/sh
[root@ip-72-167-112-17 amrood]# vi testing
root:x:0:0:root user:/root:/bin/sh
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh

使用正則表示式

在匹配模式時,您可以使用正則表示式,它提供了更大的靈活性。

檢查以下示例,該示例匹配以daemon開頭的所有行,然後刪除它們:

$ cat testing | sed '/^daemon/d'
root:x:0:0:root user:/root:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh

以下示例刪除所有以sh結尾的行:

$ cat testing | sed '/sh$/d'
sync:x:4:65534:sync:/bin:/bin/sync

下表列出了正則表示式中四個非常有用的特殊字元。

序號 字元和描述
1

^

匹配行的開頭

2

$

匹配行的結尾

3

.

匹配任何單個字元

4

*

匹配前一個字元的零個或多個出現

5

[chars]

匹配chars中給出的任何一個字元,其中chars是字元序列。您可以使用-字元表示字元範圍。

匹配字元

檢視一些其他的表示式來演示元字元的使用。例如,以下模式:

序號 表示式和描述
1

/a.c/

匹配包含a+ca-cabcmatcha3c等字串的行

2

/a*c/

匹配相同的字串以及aceyaccarctic等字串

3

/[tT]he/

匹配字串Thethe

4

/^$/

匹配空行

5

/^.*$/

匹配任何一行

6

/ */

匹配一個或多個空格

7

/^$/

匹配

下表顯示了一些常用的字元集:

序號 集合和描述
1

[a-z]

匹配單個小寫字母

2

[A-Z]

匹配單個大寫字母

3

[a-zA-Z]

匹配單個字母

4

[0-9]

[0-9]

5

匹配單個數字

[a-zA-Z0-9]

匹配單個字母或數字

字元類關鍵字

一些特殊關鍵字通常可用於正則表示式,尤其是使用正則表示式的GNU實用程式。它們對於sed正則表示式非常有用,因為它們簡化了操作並增強了可讀性。

例如,字元a到z和字元A到Z構成一類字元,其關鍵字為[[:alpha:]]

$ cat /etc/syslog.conf | sed -n '/^[[:alpha:]]/p'
authpriv.*                         /var/log/secure
mail.*                             -/var/log/maillog
cron.*                             /var/log/cron
uucp,news.crit                     /var/log/spooler
local7.*                           /var/log/boot.log

使用字母字元類關鍵字,此命令僅列印/etc/syslog.conf檔案中以字母開頭的那些行:

序號 下表是GNU sed中所有可用字元類關鍵字的完整列表。
1

字元類和描述

[[:alnum:]]

2

字母數字 [a-z A-Z 0-9]

[[:alpha:]]

3

字母 [a-z A-Z]

[[:blank:]]

4

空白字元(空格或製表符)

[[:cntrl:]]

5

控制字元

[[:digit:]]

6

數字 [0-9]

[[:graph:]]

7

任何可見字元(不包括空格)

[[:lower:]]

8

小寫字母 [a-z]

[[:print:]]

9

可列印字元(非控制字元)

[[:punct:]]

10

標點符號

[[:space:]]

11

[[:upper:]]

大寫字母 [A-Z]

12

[[:xdigit:]]

十六進位制數字 [0-9 a-f A-F]

&符號引用

sed 元字元 & 代表匹配到的模式內容。例如,假設你有一個名為 phone.txt 的檔案,其中包含以下電話號碼:

5555551212
5555551213
5555551214
6665551215
6665551216
7775551217

你想將區號(前三位數字)用括號括起來,以便於閱讀。為此,你可以使用&符號替換字元:

$ sed -e 's/^[[:digit:]][[:digit:]][[:digit:]]/(&)/g' phone.txt
(555)5551212
(555)5551213
(555)5551214
(666)5551215

(666)5551216
(777)5551217

在這裡,在模式部分,你匹配前 3 位數字,然後使用& 將這 3 位數字替換為周圍的括號

使用多個 sed 命令

你可以在單個 sed 命令中使用多個 sed 命令,如下所示:

$ sed -e 'command1' -e 'command2' ... -e 'commandN' files

這裡,command1commandN 是前面討論過的型別的 sed 命令。這些命令應用於 files 指定的檔案列表中的每一行。

使用相同的機制,我們可以將上述電話號碼示例編寫如下:

$ sed -e 's/^[[:digit:]]\{3\}/(&)/g'  \ 
   -e 's/)[[:digit:]]\{3\}/&-/g' phone.txt 
(555)555-1212 
(555)555-1213 
(555)555-1214 
(666)555-1215 
(666)555-1216 
(777)555-1217

注意 - 在上面的示例中,我們用\{3\}替換了字元類關鍵字[[:digit:]]三次重複,這意味著前面的正則表示式匹配三次。我們還使用了\來換行,在執行命令之前必須將其刪除。

反向引用

&符號元字元很有用,但更有用的是能夠在正則表示式中定義特定區域。這些特殊區域可以用作替換字串中的引用。透過定義正則表示式的特定部分,然後可以使用特殊的引用字元引用這些部分。

要進行反向引用,首先需要定義一個區域,然後引用該區域。要定義一個區域,你需要在感興趣的每個區域周圍插入反斜槓括號。你用反斜槓括起來的第一個區域由\1引用,第二個區域由\2引用,依此類推。

假設phone.txt包含以下文字:

(555)555-1212
(555)555-1213
(555)555-1214
(666)555-1215
(666)555-1216
(777)555-1217

嘗試以下命令:

$ cat phone.txt | sed 's/\(.*)\)\(.*-\)\(.*$\)/Area \ 
   code: \1 Second: \2 Third: \3/' 
Area code: (555) Second: 555- Third: 1212 
Area code: (555) Second: 555- Third: 1213 
Area code: (555) Second: 555- Third: 1214 
Area code: (666) Second: 555- Third: 1215 
Area code: (666) Second: 555- Third: 1216 
Area code: (777) Second: 555- Third: 1217

注意 - 在上面的示例中,括號內的每個正則表示式將分別由\1\2等進行反向引用。我們在這裡使用了\來換行。在執行命令之前應該將其刪除。

廣告