UNIX 正則表達(dá)式和 SED

2018-08-12 22:12 更新

正則表達(dá)式和 SED

正則表達(dá)式是一個(gè)字符串,可以用來(lái)描述幾個(gè)字符序列。UNIX 的這些命令中會(huì)用到正則表達(dá)式,包括 ed、sed、awk、grep,以及 vi。

本教程將教你如何使用正則表達(dá)式和 sed。

這里 sed 代表的流編輯器是一個(gè)面向流的編輯器,是專(zhuān)門(mén)為執(zhí)行腳本創(chuàng)建的。因此你的所有輸入都會(huì)被送到 STDOUT 并且它不改變輸入文件。

調(diào)用 sed

在我們開(kāi)始之前,讓我們以確保你有/etc/passwd文本文件的本地副本。

如前所述,可以通過(guò)一個(gè) pipe 發(fā)送數(shù)據(jù)而調(diào)用s ed,如下所示:

$ 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 命令轉(zhuǎn)儲(chǔ) /etc/passwd 的內(nèi)容到 sed 是通過(guò) pipe 進(jìn)入 sed 的模式空間。sed 使用模式空間的內(nèi)部工作緩沖區(qū)來(lái)做它的工作。

sed 的一般語(yǔ)法:

下面是 sed 的一般語(yǔ)法

    /pattern/action

在這里,pattern 是一個(gè)正則表達(dá)式,action 則是在下表中給出的命令。當(dāng)省 pattern 時(shí),如上面我們已經(jīng)看到的,action 會(huì)執(zhí)行每一行命令。

圍繞 pattern 的斜杠字符(/)是不可省略的,因?yàn)樗鼈兪亲鳛榉指舴褂谩?/p>

范圍描述
p 輸出該行
d 刪除該行
s/模式1/模式2/ 替代第一次出現(xiàn)的模式1和模式2

用 sed 刪除所有行

再次調(diào)用 sed ,但這一次使用 sed 的編輯命令刪除一行記錄,使用字母 d 表示其:

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

除了通過(guò) pipe 發(fā)送一個(gè)文件來(lái)調(diào)用 sed,你可以指導(dǎo) sed 從文件中讀取數(shù)據(jù),示例如下。

下面的命令與前面是完全一樣的,嘗試一下,里面不包括 cat 命令:

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

sed地址

sed 也可以理解為所謂的地址。地址可以是文件中的一個(gè)位置,也可以是一個(gè)特殊的編輯命令適用的范圍。當(dāng) sed 遇到?jīng)]有地址的情況時(shí),它會(huì)對(duì)文件中的每一行執(zhí)行其操作。

下面的命令將一個(gè)基本的地址添加到您使用的 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
$

注意,數(shù)字 1 添加在刪除命令前面。這告訴 sed 在文件的第一行執(zhí)行編輯命令。在這個(gè)例子中,sed將刪除/etc/password文件的第一行并打印文件的其他部分。

sed 地址范圍

所以如果你想從文件中刪除一行,您可以指定一個(gè)地址范圍如下:

$ 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
$

以上命令應(yīng)用的范圍是 1 到 5 行。所以將刪除這五行。

嘗試以下地址范圍:

范圍描述
'4,10d' 刪除第 4 到 10 行
'10,4d' 只刪除第 10 行,因?yàn)?sed 不能反方向工作
'4,+5d' 這將匹配文件中的第 4 行,刪除這一行之后,繼續(xù)刪除下一個(gè)五行,然后停止其刪除操作并輸出其他行
'2,5!d' 這將刪除除 2 到 5 行外的所有其他行。
'1~3d' 刪除第一行后,跳過(guò)接下來(lái)的三行,然后刪除第四行。sed 繼續(xù)這種模式直到文件的末尾。
'2~2d' sed 刪除第二行,跳過(guò)下一行后,刪除下面的一行,并重復(fù),直到到達(dá)文件的末尾。
'4,10p' 輸出 4 到 10 行之間的內(nèi)容。
'4,d' 產(chǎn)生語(yǔ)法錯(cuò)誤。
',10d' 也產(chǎn)生語(yǔ)法錯(cuò)誤。

注意:在使用 p action 的時(shí)候,您應(yīng)該使用 - n 選項(xiàng)來(lái)避免重復(fù)輸出。檢查以下兩個(gè)命令的 betweek 差異:

    $ cat /etc/passwd | sed -n '1,3p'

上面的命令不加 - n 的情形如下:

    $ cat /etc/passwd | sed '1,3p'

替換命令

替換命令,用 s 表示,將取代你指定的任何其他字符串。

用一個(gè)字符串替代另一個(gè),你需要告訴 sed 你第一個(gè)字符串的結(jié)束位置和想要替換的字符串的開(kāi)始位置。傳統(tǒng)上是由正斜杠(/)將兩個(gè)字符串分開(kāi)的。

以下命令將替換第一次出現(xiàn)的 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
    ..........................

非常重要的一點(diǎn)是,sed 替代只在一個(gè)命令行的某一字符串第一次出現(xiàn)時(shí)才能使用。如果字符串 root 在一行里面出現(xiàn)不止一次,只有第一個(gè) root 字符串被替換。

sed 去做一個(gè)全局替換,需要添加字母 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
    ...........................

替換標(biāo)志

除了 g 標(biāo)志外,還有許多其他有用的標(biāo)志可以使用,而且您每次可以指定多余一個(gè)標(biāo)志。

標(biāo)志描述
g 替換所有可以匹配的字符而不僅僅是第一個(gè)
NUMBER 僅僅替換第 NUMBLER 個(gè)匹配的字符
p 如果發(fā)生了替換,則輸出模式空間
w FILENAME 如果發(fā)生了替換,則將結(jié)果寫(xiě)到 FILENAME
I or i 以不區(qū)分大小寫(xiě)的方式匹配
M or m 除了擁有特殊正則表達(dá)式字符`^`和`$`的正常的行為外,這個(gè)標(biāo)志使`^`匹配換行符后的空字符串,使$匹配換行符前的空字符串。

使用一個(gè)可替換的字符串分隔符

你會(huì)發(fā)現(xiàn)自己不得不對(duì)包含斜杠字符的字符串做一個(gè)替換。在這種情況下,您可以對(duì) s 后的字符來(lái)指定一個(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

在上面的例子中:/ 作為定界符使用,而不是斜線/。因?yàn)槲覀冊(cè)噲D搜索 /root,而不是簡(jiǎn)單的 root 字符串。

使用空串的執(zhí)行替換

使用一個(gè)空的替換字符串去刪除/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

同樣的,做一個(gè)地址范圍替換,你可以做如下操作:

    $ 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,但是其他行里面的 sh 都絲毫沒(méi)有改變。

匹配命令

你可以使用 p 參數(shù)和 - n 參數(shù)輸出所有匹配的行,如下所示:

    $ 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

使用正則表達(dá)式

在進(jìn)行模式匹配時(shí),您可以使用正則表達(dá)式,它提供了更多的靈活性。

檢查下面的例子中以 daemon 開(kāi)始的行然后刪除:

    $ 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 結(jié)尾的所有行的例子:

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

下表列出了四個(gè)在正則表達(dá)式里面非常有用的特殊字符。

字符描述
^ 匹配一行的起始
$ 匹配一行的結(jié)尾
. 匹配任何的單個(gè)字符
* 匹配零個(gè)或多個(gè)以前出現(xiàn)的字符
[chars] 為了匹配任何字符串的字符。您可以使用`-`字符來(lái)表示字符的范圍。

匹配字符

來(lái)看看在其他的表達(dá)式里面如何演示元字符的使用。例如下面的模式:

表達(dá)式描述
/a.c/ 匹配包含字符串如a+c,a-c,abc, match, 還有 a3c
/a*c/ 匹配相同的字符串還有字符串比如ace,yacc,以及arctic
/[tT]he/ 匹配字符The和the
/^$/ 匹配空白行
/^.*$/ 不管任何情況,都匹配一整行
/ */ 匹配一個(gè)或多個(gè)空格
/^$/ 匹配空行

下表給出了一些常用的字符:

描述
[a-z] 匹配一個(gè)小寫(xiě)字母
[A-Z] 匹配一個(gè)大寫(xiě)字母
[a-zA-Z] 匹配一個(gè)字母
[0-9] 匹配數(shù)字
[a-zA-Z0-9] 匹配單個(gè)字母或數(shù)字

字符類(lèi)關(guān)鍵詞

通常來(lái)說(shuō),一些特殊的關(guān)鍵字對(duì) regexp 來(lái)說(shuō)也是適用的,尤其是 GNU 實(shí)用程序會(huì)使用 regexp。對(duì) sed 正則表達(dá)式來(lái)說(shuō)這些都是非常有用的,因?yàn)檫@樣既簡(jiǎn)化了表達(dá)式又增強(qiáng)了可讀性。

例如,字符 a 到 z 以及字符 A 到 Z 構(gòu)成了這樣一個(gè)用關(guān)鍵字[[:alpha:]]表示的類(lèi)。

使用字母表的字符類(lèi)關(guān)鍵詞,這個(gè)命令輸出/etc/syslog.conf文件里面以字母表的字母開(kāi)始的行:

    $ 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

下表是 GNU sed 的可用的字符類(lèi)關(guān)鍵詞的一個(gè)完整的列表。

字符類(lèi)描述
[[:alnum:]] 字母(a - z A-Z 0 - 9)
[[:alpha:]] 字母(a - z A-Z)
[[:blank:]] 空白字符(空格或制表鍵)
[[:cntrl:]] 控制字符
[[:digit:]] 數(shù)字[0 - 9]
[[:graph:]] 任何可見(jiàn)字符(不包括空格)
[[:lower:]] 小寫(xiě)字母的[a -?]
[[:print:]] 可打印字符(無(wú)控字符)
[[:punct:]] 標(biāo)點(diǎn)字符
[[:space:]] 空白
[[:upper:]] 大寫(xiě)字母的[A -Z]
[[:xdigit:]] 十六進(jìn)制數(shù)字[0 - 9 a - f A-F]

&引用

sed 元字符 & 代表被匹配的 pattern 的內(nèi)容。例如,假設(shè)您有一個(gè)名為 phone.txt 的文件,里面都電話號(hào)碼,如下所示:

    5555551212

    5555551213

    5555551214

    6665551215

    6665551216

    7775551217

你想讓前三個(gè)數(shù)字被括號(hào)括起來(lái)以更容易閱讀。要做到這一點(diǎn),您可以使用 & 替換字符,如下所示:

    $ sed - e ' s / ^[[數(shù)位:]][[數(shù)位:]][[數(shù)位:]](&)/ g phone.txt

    (555)5551212

    (555)5551213

    (555)5551214

    (666)5551215

    (666)5551216

    (777)5551217

先匹配 3 位數(shù)字,然后使用 & 取代那些括號(hào)括起來(lái)的數(shù)字。

使用多個(gè) sed 命令

您可以在一個(gè) sed 命令下使用多個(gè) sed 命令,如下:

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

這里 commandN 到 command1 都是我們之前討論的 sed 類(lèi)型命令。這些命令應(yīng)用于每個(gè)文件列表的行。

以相同的機(jī)制,我們可以以下面的方式寫(xiě)上面的電話號(hào)碼:

    $ sed - e ' s / ^[[數(shù)位:]]\ \ { 3 } /(&)/ g \

                             - e ' s /)[[數(shù)位:]]\ \ { 3 } / & - / g phone.txt

    (555)555 - 1212

    (555)555 - 1213

    (555)555 - 1214

    (666)555 - 1215

    (666)555 - 1216

    (777)555 - 1217

注意:在上面的例子中,不是重復(fù)字符類(lèi)關(guān)鍵字[[:digit:]] 三次,而是代之以\{3\},這意味著前三次正則表達(dá)式相匹配。

引用

& 元字符是有用的,但更有用的功能是能夠在正則表達(dá)式中定義特定區(qū)域,通過(guò)定義正則表達(dá)式的特定的一部分,您可以引用字符引用這部分。

反向引用時(shí),你必須首先定義一個(gè)區(qū)域,然后回顧這個(gè)區(qū)域。定義一個(gè)區(qū)域是在你感興趣的區(qū)域插入\和括號(hào)。你周?chē)牡谝粎^(qū)域被通過(guò)\ 1引用,第二個(gè)地區(qū)用 \ 2引用,等等。

假設(shè) phone.txt 有以下文本:

    (555)555 - 1212

    (555)555 - 1213

    (555)555 - 1214

    (666)555 - 1215

    (666)555 - 1216

    (777)555 - 1217

現(xiàn)在試試下面的命令:

    $ 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

注意:在上面的例子中每個(gè)括號(hào)內(nèi)的正則表達(dá)式將引用\ 1,,\ 2等等。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)