在本章中,我们将详细讨论Unix中使用SED的正则表达式。
正则表达式是一个字符串,可用于描述几个字符序列。正则表达式由几个不同的Unix命令使用,包括ed,sed,awk,grep以及在更有限的范围内vi。
这里SED代表stream monitor,这个面向流的编辑器是专门为执行脚本而创建的。因此,您输入的所有输入都会通过并进入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,则如上所述,将对每一行执行操作。
包围pattern的斜杠(/)是必需的,因为它们用作分隔符。
序号 | 范围和说明 |
---|---|
1 | p输出行 |
2 | d删除行 |
3 | s/模式1/模式2/用pattern2替换第一次出现的pattern1 |
用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行匹配,删除该行,继续删除接下来的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'
Check the above command without -n as follows −
$ cat /etc/passwd | sed '1,3p'
替换命令
用s表示的替换命令将用您指定的任何其他字符串替换您指定的任何字符串。
要用一个字符串替换另一个字符串,sed需要具有有关第一个字符串结束和替换字符串开始的位置的信息。为此,我们继续使用正斜杠(/)字符隔开两个字符串。
以下命令用字符串amrood替换字符串root的一行上的第一个匹配项。
$ 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标志之外,还可以传递许多其他有用的标志,并且您一次可以指定多个。
序号 | 标志和说明 |
---|---|
1 | g替换所有匹配项,而不仅仅是第一场匹配项 |
2 | NUMBER仅替换第NUMBER个匹配项 |
3 | p如果进行替换,则输出pattern 匹配的行 |
4 | w 文件名如果进行了替换,则将结果写入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文件中完全删除根字符串-
$ cat /etc/passwd | sed 's/root//g'
:x:0:0::/:/bin/sh
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
地址替换
如果只想在第10行用字符串quiet替换字符串sh,则可以如下指定:
$ 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 + c,ac,abc,match和a3c之类的字符串的行 |
2 | /a*c/匹配相同的字符串以及诸如ace,yacc和arctic之类的字符串 |
3 | /[tT]he/匹配字符串The和the |
4 | /^$/匹配空白行 |
5 | /^.*$/匹配整行 |
6 | / */匹配一个或多个空格 |
7 | /^$/匹配空白行 |
下表显示了一些常用的字符集-
序号 | 设置与说明 |
---|---|
1 | [az]匹配单个小写字母 |
2 | [A-Z]匹配单个大写字母 |
3 | [a-zA-Z]匹配一个字母 |
4 | [0-9]匹配一个数字 |
5 | [a-zA-Z0-9]匹配单个字母或数字 |
字符类别关键字
正则表达式通常可以使用一些特殊的关键字,尤其是采用正则表达式的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:]]字母数字[az AZ 0-9] |
2 | [[:alpha:]]字母[az AZ] |
3 | [[:blank:]]空白字符(空格或制表符) |
4 | [[:cntrl:]]控制字符 |
5 | [[:digit:]]数字[0-9] |
6 | [[:graph:]]任何可见字符(不包括空格) |
7 | [[:lower:]]小写字母[az] |
8 | [[:print:]]可打印字符(非控制字符) |
9 | [[:punct:]]标点符号 |
10 | [[:space:]]空格 |
11 | [[:upper:]]大写字母[AZ] |
12 | [[:xdigit:]]十六进制数字[0-9 AF后] |
&引用
所述的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:]]三次,而是将其替换为{3},这意味着前面的正则表达式被匹配了3次。我们还使用**给出了换行符,必须在运行命令之前将其删除。
反向参考
“ & ”符号是有用的,但更有用的是在正则表达式中定义特定区域的功能。这些特殊区域可以用作替换字符串中的参考。通过定义正则表达式的特定部分,然后可以使用特殊的参考字符来引用这些部分。
要回溯引用,您必须先定义一个区域,然后再返回该区域。要定义区域,请在每个感兴趣的区域周围插入反斜杠。然后,您用反斜杠包围的第一个区域由\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: Second: Third: /'
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等反向引用。我们在这里使用**来换行。在运行命令之前,应将其删除。