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等进行反向引用。我们在这里使用了\来换行。在运行命令之前应该将其删除。

广告