上一章Unix和Linux教程请查看:Unix和Linux的vi编辑器操作和用法
在本章中我们将详细讨论在Unix中使用SED的正则表达式。正则表达式是一个可以用来描述多个字符序列的字符串,正则表达式由几个不同的Unix命令使用,包括ed、sed、awk、grep,在一定程度上还包括vi。
这里SED代表流编辑器(stream editor),这个面向流的编辑器是专门为执行脚本而创建的。因此我们提供给它的所有输入都经过并进入STDOUT,并且它不会更改输入文件。
1、调用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用于其操作的内部工作缓冲区。
2、sed通用语法
以下是sed的一般语法:
/pattern/action
在这里pattern是一个正则表达式,action是下表中给出的命令之一。如果模式被省略将对上面看到的每一行执行操作,需要模式周围的斜杠字符(/),因为它们用作分隔符。
编号 | 访问 & 描述 |
1 | p 打印行 |
2 | d 删除行 |
3 | s/pattern1/pattern2/ 用pattern2代替pattern1的第一次出现 |
3、使用sed删除所有行
现在我们将了解如何使用sed删除所有行。再次调用sed但是sed现在应该使用编辑命令delete行,用单个字母d−表示:
$ cat /etc/passwd | sed 'd'
$
与通过管道向sed发送文件不同,可以指示sed从文件中读取数据,如下面的示例所示下面的命令与前面的示例完全相同,但是没有使用cat命令:
$ sed -e 'd' /etc/passwd
$
4、sed地址
sed还支持地址。地址是文件中的特定位置或应用特定编辑命令的范围,当sed没有遇到地址时,它将对文件中的每一行执行操作。下面的命令将向你一直使用的sed命令添加基本地址:
$ cat /etc/passwd | sed '1d' |more
user1:x:1:1:user1:/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是在delete编辑命令之前添加的。这指示sed在文件的第一行执行编辑命令,在本例中sed将删除/etc/password的第一行并打印文件的其余部分。
编号 | 范围 & 描述 |
1 | ‘4,10d’ 从第4行到第10行被删除 |
2 | ‘10,4d’ 只有第10行被删除,因为sed没有反向工作 |
3 | ‘4,+5d’ 它匹配文件中的第4行并删除该行,继续删除后面的5行,然后停止删除并打印其余的行 |
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'
$ cat /etc/passwd | sed '1,3p'
5、替换命令
用s表示的替换命令将用指定的其他字符串替换指定的任何字符串。要用一个字符串替换另一个字符串,sed需要知道第一个字符串的结束位置和替换字符串的开始位置,为此我们使用正斜杠(/)字符对两个字符串进行辅助。下面的命令用字符串user替换字符串根行的第一个匹配项。
$ cat /etc/passwd | sed 's/root/user/'
user:x:0:0:root user:/root:/bin/sh
user1:x:1:1:user1:/usr/sbin:/bin/sh
..........................
需要注意的是sed仅替换行上的第一个事件。如果字符串根在一行中出现多次,则只替换第一个匹配项。为了让sed执行全局替换,请在命令末尾添加字母g,如下所示:
$ cat /etc/passwd | sed 's/root/user/g'
user:x:0:0:user user:/user:/bin/sh
user1:x:1:1:user1:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
...........................
6、替换标志
除了g标志之外还可以传递许多其他有用的标志,你可以一次指定多个标志。
编号 | 标志 & 描述 |
1 | g 替换所有匹配项,而不仅仅是第一个匹配项 |
2 | NUMBER 只替换第NUMBER个匹配 |
3 | p 如果进行了替换,则打印模式空间 |
4 | w FILENAME 如果进行了替换,则将结果写入文件名 |
5 | I or i 以大小写不敏感的方式匹配 |
6 | M or m 除了特殊的正则表达式字符^和$的正常行为之外,这个标志还会导致^匹配换行后的空字符串,而$匹配换行前的空字符串 |
7、使用可选的字符串分隔符
假设必须对包含正斜杠字符的字符串进行替换。在这种情况下可以通过在s后面提供指定的字符来指定不同的分隔符。
$ cat /etc/passwd | sed 's:/root:/user:g'
user:x:0:0:user user:/user:/bin/sh
user1:x:1:1:user1:/usr/sbin:/bin/sh
在上面的例子中,我们使用:作为分隔符而不是斜杠/,因为我们试图搜索/root而不是简单的根。
8、替换空白
使用空替换字符串从/etc/passwd文件中完全删除根字符串:
$ cat /etc/passwd | sed 's/root//g'
:x:0:0::/:/bin/sh
user1:x:1:1:user1:/usr/sbin:/bin/sh
9、地址替换
如果希望仅在第10行使用字符串quiet替换字符串sh,可以按如下方式指定它:
$ cat /etc/passwd | sed '10s/sh/quiet/g'
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
类似地要执行地址范围替换可以执行以下操作:
$ cat /etc/passwd | sed '1,5s/sh/quiet/g'
bin:x:2:2:bin:/bin:/bin/quiet
sys:x:3:3:sys:/dev:/bin/quiet
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
正如你从输出中看到的,前5行将字符串sh更改为quiet,但其余行保持不变。
10、替换命令
可以使用p选项和-n选项来打印所有匹配的行,如下所示:
$ cat testing | sed -n '/root/p'
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
11、使用正则表达式
在匹配模式时可以使用提供更多灵活性的正则表达式。检查下面的示例,它匹配所有以daemon开头的行然后删除它们:
$ cat testing | sed '/^usx/d'
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
下表列出了在正则表达式中非常有用的四个特殊字符。
编号 | 字符 & 描述 |
1 | ^ 匹配行首 |
2 | $ 匹配行尾 |
3 | . 匹配任何单个字符 |
4 | * 匹配前一个字符的零次或多次出现 |
5 | [chars] 匹配字符中的任何一个字符,其中字符是字符序列。可以使用-字符来表示字符的范围。 |
12、字符匹配
再看几个表达来演示元字符的用法。例如下面的模式:
编号 | 表达式 & 描述 |
1 | /a.c/ 匹配包含字符串(如a+c、a-c、abc、match和a3c)的行 |
2 | /a*c/ 与ace、yacc和arctic等字符串匹配相同的字符串 |
3 | /[tT]he/ 匹配字符串the和the |
4 | /^$/ 匹配空行 |
5 | /^.*$/ 匹配一整行 |
6 | / */ 匹配一个或多个空格 |
7 | /^$/ 匹配空行 |
下表显示了一些常用的字符集:
编号 | 集合 & 描述 |
1 | [a-z] 匹配单个小写字母 |
2 | [A-Z] 匹配单个大写字母 |
3 | [a-zA-Z] 匹配单个字母 |
4 | [0-9] 匹配单个数字 |
5 | [a-zA-Z0-9] 匹配单个字母或数字 |
13、字符类关键字
一些特殊的关键字通常可用于regexp,特别是使用regexp的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中可用字符类关键字的完整列表。
编号 | 字符类 & 描述 |
1 | [[:alnum:]] 字母数字[a-z A-Z 0-9] |
2 | [[:alpha:]] 字母 [a-z A-Z] |
3 | [[:blank:]] 空白字符(空格或制表符) |
4 | [[:cntrl:]] 控制字符 |
5 | [[:digit:]] 数字[0-9] |
6 | [[:graph:]] 任何可见字符(不包括空格) |
7 | [[:lower:]] 小写字母[a-z] |
8 | [[:print:]] 可打印字符(非控制字符) |
9 | [[:punct:]] 标点符号 |
10 | [[:space:]] 空格 |
11 | [[:upper:]] 大写字母[A-Z] |
12 | [[:xdigit:]] 十六进制数字[0-9 a-f A-F] |
14、&引用
sed元字符&表示匹配的模式的内容。例如假设有一个名为phone.txt的文件,其中充满了电话号码,如下所示:
9999991212
9999991213
为了便于阅读需要将前三位数用圆括号括起来,为此可以使用与符号替换字符:
$ sed -e 's/^[[:digit:]][[:digit:]][[:digit:]]/(&)/g' phone.txt
(999)9991212
(999)9991213
在模式部分中,首先匹配前3位数字,然后使用&这样你就可以用括号里的3位数字代替。
15、使用多个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
(999)999-1212
(666)999-1213
注意在上面的例子中,我们用\{3\}替换了字符类关键字[[:digit:]]三次,这意味着前面的正则表达式匹配了三次。我们还使用\给出换行符,在运行命令之前必须将其删除。
16、反向引用
与&元字符很有用,但更有用的是在正则表达式中定义特定区域的能力。这些特殊区域可以在替换字符串中用作参考,通过定义正则表达式的特定部分,可以使用特殊的引用字符引用这些部分。
要实现泛型引用,你必须首先定义一个区域,然后再回引用该区域。要定义一个区域可以在每个感兴趣的区域周围插入反斜杠括号,使用反斜杠包围的第一个区域将由\1引用,第二个区域将由\2引用依此类推。
评论前必须登录!
注册