Unix / Linux - 带SED的正则表达式

在本章中,我们将详细讨论在Unix中使用SED的正则表达式.

正则表达式是一个字符串,可用于描述几个字符序列.正则表达式由几个不同的Unix命令使用,包括 ed sed awk grep ,以及更有限的范围, vi .

此处 SED 代表 s tream ed itor.这个面向流的编辑器专门用于执行脚本.因此,您输入的所有输入都会通过并转到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&minus的一般语法;

/pattern/action

在这里, pattern 是正则表达式, action 是下表中给出的命令之一.如果省略 pattern ,则如上所示,对每一行执行 action .

周围的斜杠字符(/)模式是必需的,因为它们被用作分隔符.

Sr.No.Range&描述
1

p

打印行

2

d

删除行

3

s/pattern1/pattern2/

用pattern2替换第一次出现的pattern1

使用sed删除所有行

我们现在将了解如何用sed删除所有行.再次调用sed;但是sed现在应该使用编辑命令删除行,用单个字母 d :

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

不是通过管道向文件发送文件来调用sed,而是可以指示sed读取数据来自文件,如下例所示.

以下命令与前一个示例完全相同,没有cat命令 :

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

sed Addresses

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开始的所有行.这将删除前五行.

尝试以下地址范围 :

Sr.No.范围&描述
1

'4,10d'

删除从4 th 开始直到10 th 的行

2

'10,4d'

只删除10个 th 行,因为sed没有反向工作

3

'4,+ 5d'

这与文件中的第4行匹配,删除该行,继续删除下五行,然后停止删除并打印剩余部分

4

'2,5!d'

这删除除2 nd 至5 th 行之外的所有内容

5

'1~3d'

这将删除第一行,逐步执行接下来的三行,然后删除第四行. Sed继续应用此模式直到文件结束.

6

'2~2d'

这告诉sed删除第二行,跳过下一行,删除下一行,然后重复,直到到达文件末尾

7

'4,10p'

打印从4 th 到10 th 的行

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需要获取有关第一个字符串结束位置的信息.替换字符串开始.为此,我们继续使用正斜杠(/)字符对两个字符串进行bookending.

以下命令替换字符串行上的第一个匹配项 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只替换一行中的第一个匹配项.如果字符串根在一行上出现多次,则只会替换第一个匹配.

对于要执行全局替换的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 标志外,还有许多其他有用的标志可以传递,你可以一次指定多个.

Sr.No.Flag&描述
1

g

取代所有比赛,而不仅仅是第一场比赛

2

NUMBER

仅替换NUMBER th 匹配

3

p

如果进行了替换,然后打印模式空间

4

w FILENAME

如果进行了替换,则将结果写入FILENAME

5

I or i

以不区分大小写的方式匹配

6

M或m

除了特殊正则表达式字符^和$的正常行为外,此标志还会导致^与empt匹配换行后的字符串和$换行符之前的空字符串

使用替代字符串分隔符

假设您必须对包含正斜杠字符的字符串进行替换.在这种情况下,您可以通过在 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 而不是简单的根.

用空格替换

使用空替换字符串从/etc/passwd 文件中完全删除根字符串 :

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

地址替换

如果要用字符串<替换字符串 sh b>安静仅在第10行,你可以指定如下 :

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

使用正则表达式

在匹配模式时,您可以使用提供更大灵活性的正则表达式.

检查以下示例,该示例匹配以

$ 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

以下是删除所有以 sh &minus结尾的行的示例;

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

下表列出了四个在正则表达式中非常有用的特殊字符.

Sr.No.Character&描述
1

^

匹配行的开头

2

$

匹配行尾

3

.

匹配任何单个字符

4

*

匹配前一个字符的零次或多次出现

5

[chars]

匹配chars中给出的任何一个字符,其中chars是一系列字符.您可以使用 - 字符来表示一系列字符.

匹配字符

再看几个表达式来演示使用元字符.例如,以下模式 :

Sr.No.表达式&描述
1

/ac/

匹配包含字符串的行,例如 a&plus; c ac abc 匹配 a3c

2

/a * c/

匹配相同的字符串以及字符串,例如 ace yacc arctic

3

/[tT]he/

匹配字符串

4

/^ $/

匹配空行

5

/^.*$/

匹配整行不管它是什么

6

/*/

匹配一个或多个空格

7

/^ $/

匹配空白

下表显示了一些经常使用的字符集 :

Sr.No.Set&描述
1

[az]

匹配单个小写字母

2

[AZ]

匹配单个大写字母

3

[a-zA-Z]

匹配一个字母

4

[0-9]

匹配单个数字

5

[a-zA-Z0 -9]

匹配单个字母或数字

字符类关键字

regexps 通常可以使用一些特殊关键字,尤其是使用 regexps的GNU实用程序.这些对于sed正则表达式非常有用,因为它们可以简化并提高可读性.

例如,字符 a到z 和字符 A到Z ,构成一个这样的一类字符,其关键字为 [[:alpha:]]

使用字母字符类关键字,此命令仅打印/etc/syslog.conf 文件中以字母开头的字母开头的那些行 :

$ 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中可用字符类关键字的完整列表.

Sr.No.Character Class&描述
1

[[:alnum:]]

字母数字[az AZ 0-9]

2

[[:alpha: ]]

字母[az AZ]

3

[[:blank:]]

空白字符(空格或制表符)

4

[[:cntrl:]]

控制字符

5

[[:数字:]]

数字[0-9]

6

[[: graph:]]

任何可见的字符(不包括空格)

7

[[:lower:]]

小写字母[az]

8

[[:print:]]

可打印字符(非控制字符)

9

[[:punct:]]

标点字符

10

[[:space:]]

空白

11

[[: upper:]]

大写字母[AZ]

12

[[:xdigit:]]

十六进制数字[0-9 af AF]

Aampersand Referencing

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

这里 command1 commandN 是前面讨论过的类型的sed命令.这些命令应用于文件给出的文件列表中的每一行.

使用相同的机制,我们可以将上面的电话号码示例编写如下 :

$ 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 反向引用,依此类推.我们在这里使用了 \ 来换行.在运行命令之前应该删除它.