龙空技术网

Shell编程基础(九)Shell三剑客之sed

IT菜鸟185 122

前言:

此刻看官们对“centos74ssh升级”大致比较注意,咱们都需要知道一些“centos74ssh升级”的相关资讯。那么小编在网摘上搜集了一些关于“centos74ssh升级””的相关内容,希望各位老铁们能喜欢,小伙伴们一起来学习一下吧!

概述

sed是贝尔实验室的Lee E.McMahon在1973年到1974年开发的流编辑器,sed是基于交互式编辑器ed开发的软件,sed与ed一样也是行处理编辑器。sed是最早开始支持正则表达式的工具之一,我们可以使用sed非常轻松地完成非交互式的文件编辑工作,包括但不限于对文件的增、删、改、查等操作。本文将先介绍sed的基本指令,在下篇文章将继续介绍sed的高级指令。

sed数据处理流程

如上图所示,sed会逐行扫描输入的数据,并将读取的数据内容复制到缓冲区中,我们称之为模式空间,然后拿模式空间中的数据与给定的条件进行匹配,匹配成功则执行特定的sed指令,否则sed会跳过输入的数据行,继续读取后续的数据。默认情况下sed会把数据结果通过标准输出显示在屏幕上。

sed基本语法语法格式:

命令 | sed [选项] ‘匹配条件和操作指令’sed [选项] ‘匹配条件和操作指令’ 输入文件...
常用的命令选项:-n,--silent:屏蔽默认输出(默认sed会把匹配到的数据显示在屏幕上)-r:支持扩展正则-i[SUFFIX]:直接修改源文件,如果设置了SUFFIX后缀名,sed会将数据备份-e:指定需要执行的sed指令,支持使用多个-e参数-f:指定需要执行的脚本文件,需要提前将sed指令写入文件中sed基本操作指令:p:打印当前匹配的数据行l(小写L):打印当前匹配的数据行(显示控制字符,如回车符等)a text:在匹配的数据行后面追加内容i text:在匹配的数据行前面插入内容d:删除匹配的数据行整行内容(行删除)c text:将匹配的数据行整行内容替换为特定的文本内容r filename:从文件中读取数据并追加到匹配的数据行后面w filename:将当前匹配到的数据写入指定的文件中q [exit code]:立刻退出sed脚本s/regexp/replace/:使用正则匹配,将匹配到的数据替换为指定得到内容

sed指令执行前需要先根据条件定位需要处理的数据行,如果没有指定定位条件,则默认sed会对所有数据执行特定的指令。

sed支持的数据定位方式:

行号定位:

Number:直接根据行号匹配数据First~step:从first行开始,步长为step,匹配所有满足条件的数据行n1p;n2p;...nnp:精准匹配n1、n2、...、n3行数据addr1,addr2:直接使用行号定位,匹配从addr1到addr2的所有行Addr1,+N:直接使用行号定位,匹配从addr1开始及后面的N行

# sed默认将匹配到的内容显示在屏幕上,p指令将匹配到的数据行显示在屏幕上,没有指定定位方式默认匹配所有行,故打印两次[root@localhost sed]# sed 'p' /etc/hosts127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4::1 localhost localhost.localdomain localhost6 localhost6.localdomain6::1 localhost localhost.localdomain localhost6 localhost6.localdomain6# 使用-n选项屏蔽sed的默认输出[root@localhost sed]# sed -n 'p' /etc/hosts127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4::1 localhost localhost.localdomain localhost6 localhost6.localdomain6# 使用行定位显示第1行内容[root@localhost sed]# sed -n '1p' /etc/hosts127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4# 仅显示第2行内容[root@localhost sed]# sed -n '2p' /etc/hosts::1 localhost localhost.localdomain localhost6 localhost6.localdomain6将df指令的结果通过管道传递给sed仅显示第2行内容[root@localhost sed]# df -h | sed -n '2p'/dev/mapper/centos-root 39G 24G 16G 61% /[root@localhost sed]# free -h | sed -n '2p'Mem: 1.8G 140M 1.5G 9.5M 163M 1.5G[root@localhost sed]#
# 在/etc/passwd文件每行的开头添加上行号,重定向到当前目录的passwd文件中[root@localhost sed]# cat -n /etc/passwd > passwd# 打印第1行到第3行内容[root@localhost sed]# sed -n '1,3p' passwd1 root:x:0:0:root:/root:/bin/bash2 bin:x:1:1:bin:/bin:/sbin/nologin3 daemon:x:2:2:daemon:/sbin:/sbin/nologin# 从第3行开始所有的奇数行内容[root@localhost sed]# sed -n '3~2p' passwd3 daemon:x:2:2:daemon:/sbin:/sbin/nologin5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin11 games:x:12:100:games:/usr/games:/sbin/nologin13 nobody:x:99:99:Nobody:/:/sbin/nologin15 dbus:x:81:81:System message bus:/:/sbin/nologin17 sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin19 nginx:x:1000:1000::/home/nginx:/sbin/nologin21 www:x:1002:1002::/home/www:/sbin/nologin# 打印第1、第3、第6行的内容[root@localhost sed]# sed -n '1p;3p;6p' passwd1 root:x:0:0:root:/root:/bin/bash3 daemon:x:2:2:daemon:/sbin:/sbin/nologin6 sync:x:5:0:sync:/sbin:/bin/sync# 打印第4行及后面两行的内容[root@localhost sed]# sed -n '4,+2p' passwd4 adm:x:3:4:adm:/var/adm:/sbin/nologin5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin6 sync:x:5:0:sync:/sbin:/bin/sync[root@localhost sed]#

正则定位:

$:匹配最后一行/regexp/:使用正则表达式匹配数据行\cregexpc:使用正则表达式匹配数据行,c可以是任意字符

# 打印最后一行[root@localhost sed]# sed -n '$p' passwd21 www:x:1002:1002::/home/www:/sbin/nologin# 匹配包含root的行[root@localhost sed]# sed -n '/root/p' passwd1 root:x:0:0:root:/root:/bin/bash10 operator:x:11:0:operator:/root:/sbin/nologin# 匹配以root开头的行[root@localhost sed]# sed -n '/^root/p' /etc/passwdroot:x:0:0:root:/root:/bin/bash# 匹配以bash结尾的行[root@localhost sed]# sed -n '/bash$/p' passwd1 root:x:0:0:root:/root:/bin/bash# 匹配包含root或www的行[root@localhost sed]# sed -rn '/(root|www)/p' passwd1 root:x:0:0:root:/root:/bin/bash10 operator:x:11:0:operator:/root:/sbin/nologin21 www:x:1002:1002::/home/www:/sbin/nologin

在sed中支持使用感叹号(!)对匹配的条件取反操作,使用等号(=)结合条件匹配,可以显示特定数据行的行号

# 匹配除了以nologin结尾以外的其他所有行[root@localhost sed]# sed -n '/nologin$/!p' passwd1 root:x:0:0:root:/root:/bin/bash6 sync:x:5:0:sync:/sbin:/bin/sync7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown8 halt:x:7:0:halt:/sbin:/sbin/halt# sed默认使用斜杠(/)作为定位符和指令的分隔符及替换操作时的替换符号,sed同时也支持使用任何其他字符作为分隔符或替换符[root@localhost sed]# sed -n '\;root;p' passwd1 root:x:0:0:root:/root:/bin/bash10 operator:x:11:0:operator:/root:/sbin/nologin[root@localhost sed]# sed -n '\cwwwcp' passwd21 www:x:1002:1002::/home/www:/sbin/nologin[root@localhost sed]# sed -n '\2nginx2p' passwdnginx:x:1000:1000::/home/nginx:/sbin/nologin# 打印包含root的行号[root@localhost sed]# sed -n '/root/=' passwd110#打印第3行的行号[root@localhost sed]# sed -n '3=' passwd3# 打印最后一行的行号[root@localhost sed]# sed -n '$=' passwd21
sed指令的基本使用a指令的使用

使用a指令在匹配到的数据行后面追加数据行

[root@localhost sed]# cp /etc/hosts ./[root@localhost sed]# lshosts passwd# 在第1行后面追加数据[root@localhost sed]# sed '1a add test line' hosts127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4add test line::1 localhost localhost.localdomain localhost6 localhost6.localdomain6# 在包含::1的行后面追加数据[root@localhost sed]# sed '/::1/a insert data' hosts127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4::1 localhost localhost.localdomain localhost6 localhost6.localdomain6insert data

上面的示例中没有使用-i选项,不会对源文件做任何更改,仅仅是在缓存区中修改了数据并显示出来。

如果希望直接修改源文件可以添加-i选项。

[root@localhost sed]# sed -i '/::1/a insert data' hosts[root@localhost sed]# cat hosts127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4::1 localhost localhost.localdomain localhost6 localhost6.localdomain6insert data
如果-i选项后面添加了SUFFIX后缀,则会先将源文件进行备份,再修改源文件的内容。# 先将源文件备份,再删除最后一行的数据[root@localhost sed]# sed -i.bak '$d' hosts[root@localhost sed]# cat hosts127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4::1 localhost localhost.localdomain localhost6 localhost6.localdomain6[root@localhost sed]# lshosts hosts.bak passwd[root@localhost sed]# cat hosts.bak127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4::1 localhost localhost.localdomain localhost6 localhost6.localdomain6insert data
i指令的使用

使用i指令可以在匹配行的前面插入数据行

[root@localhost sed]# sed '1i insert data before the first row' hostsinsert data before the first row127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4::1 localhost localhost.localdomain localhost6 localhost6.localdomain6[root@localhost sed]#

需要注意的是,a或者i指令后面的所有内容都会被理解为需要添加的内容,因此其后面不可以再写其他的指令。

# a指令后面的所有内容都被追加到包含::1的行后面[root@localhost sed]# sed '/::1/a append new line;2p;2i insert new line' hosts127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4::1 localhost localhost.localdomain localhost6 localhost6.localdomain6append new line;2p;2i insert new line[root@localhost sed]#
d指令的使用

使用d指令删除数据行,如果没有添加任何数据定位方式,则表示删除文件中的所有数据。

[root@localhost sed]# cat hosts127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4::1 localhost localhost.localdomain localhost6 localhost6.localdomain6[root@localhost sed]# sed 'd' hosts[root@localhost sed]# sed '1d' hosts::1 localhost localhost.localdomain localhost6 localhost6.localdomain6[root@localhost sed]# sed '/127/d' hosts::1 localhost localhost.localdomain localhost6 localhost6.localdomain6[root@localhost sed]#

使用-i选项可以直接修改源文件

# 复制素材文件[root@localhost sed]# cp /etc/profile ./# 原始文件中共有76行[root@localhost sed]# cat profile | wc -l76# 删除空白行后还剩65行[root@localhost sed]# sed -i '/^$/d' profile[root@localhost sed]# cat profile | wc -l65# 删除以#开头的行后还剩53行[root@localhost sed]# sed -i '/^#/d' profile[root@localhost sed]# cat profile | wc -l53[root@localhost sed]#
c指令的使用

使用c指令替换整行的内容,不指定任何定位方式,则默认替换所有行的内容

[root@localhost sed]# cat hosts127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4::1 localhost localhost.localdomain localhost6 localhost6.localdomain6# 替换所有行的内容[root@localhost sed]# sed 'c modify data for all rows' hostsmodify data for all rowsmodify data for all rows# 替换第2行的内容[root@localhost sed]# sed '2c modify the data in the second row' hosts127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4modify the data in the second row# 替换最后一行的内容[root@localhost sed]# sed '$c modify the data of the last row' hosts127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4modify the data of the last row[root@localhost sed]#
r指令和w指令的使用

使用r指令可以将其他文件的内容读取并存入当前需要编辑的文件中,而w指令则将当前编辑的文件内容另存到其他文件中,如果目标文件已存在,则另存时将覆盖目标文件的内容。

如果不指定任何定位方式,则会将其他文件的内容存入到当前需要编辑的文件的每一行后面。

[root@localhost sed]# cat /etc/hostnamelocalhost.localdomain[root@localhost sed]# cat hosts127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4::1 localhost localhost.localdomain localhost6 localhost6.localdomain6# 在hosts文件的每一行后面读入/etc/hostname文件中的内容[root@localhost sed]# sed 'r /etc/hostname' hosts127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4localhost.localdomain::1 localhost localhost.localdomain localhost6 localhost6.localdomain6localhost.localdomain# 在最后一行读入内容[root@localhost sed]# sed '$r /etc/hostname' hosts127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4::1 localhost localhost.localdomain localhost6 localhost6.localdomain6localhost.localdomain# 在包含127的行后面读入内容[root@localhost sed]# sed '/127/r /etc/hostname' hosts127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4localhost.localdomain::1 localhost localhost.localdomain localhost6 localhost6.localdomain6[root@localhost sed]#
[root@localhost sed]# cat hosts127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4::1 localhost localhost.localdomain localhost6 localhost6.localdomain6# 将hosts中的所有内容另存到myhost文件中[root@localhost sed]# sed 'w myhost' hosts127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4::1 localhost localhost.localdomain localhost6 localhost6.localdomain6[root@localhost sed]# cat myhost127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4::1 localhost localhost.localdomain localhost6 localhost6.localdomain6# 将/etc/shells文件中的第1行到第3行的内容另存到myshell文件中[root@localhost sed]# sed '1,3w myshell' /etc/shells/bin/sh/bin/bash/sbin/nologin/usr/bin/sh/usr/bin/bash/usr/sbin/nologin[root@localhost sed]# cat myshell/bin/sh/bin/bash/sbin/nologin[root@localhost sed]#
q指令的使用

正常情况下sed会在读取完所有数据行之后退出,但是我们可以使用q指令来提前退出sed。

# 读取文件的第3行时退出sed[root@localhost sed]# sed '3q' passwd1 root:x:0:0:root:/root:/bin/bash2 bin:x:1:1:bin:/bin:/sbin/nologin3 daemon:x:2:2:daemon:/sbin:/sbin/nologin[root@localhost sed]#

注意:一般不要在使用类似于3q之类的指令时同时使用-i选项,这样会导致sed使用读取出来的3行数据,覆盖源文件,从而导致源文件中其他数据的丢失。

s指令的使用

前面我们介绍的使用c和d对数据进行修改和删除操作,都是以行为单位进行的,但是我们在实际的工作中常常需要的是将某个关键词替换,或者将某个关键词删除,此时就需要使用s指令来完成这样的工作。

[root@localhost sed]# cat hosts127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4::1 localhost localhost.localdomain localhost6 localhost6.localdomain6# 将每行的host替换成hello[root@localhost sed]# sed 's/host/hello/' hosts127.0.0.1 localhello localhost.localdomain localhost4 localhost4.localdomain4::1 localhello localhost.localdomain localhost6 localhost6.localdomain6# 仅替换第一行的host[root@localhost sed]# sed '1s/host/hello/' hosts127.0.0.1 localhello localhost.localdomain localhost4 localhost4.localdomain4::1 localhost localhost.localdomain localhost6 localhost6.localdomain6# 替换包含::1行的host[root@localhost sed]# sed '/::1/s/host/hello/' hosts127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4::1 localhello localhost.localdomain localhost6 localhost6.localdomain6# 替换最后一行的host[root@localhost sed]# sed '$s/host/hello/' hosts127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4::1 localhello localhost.localdomain localhost6 localhost6.localdomain6[root@localhost sed]#

通过上面的示例,我们发现关键词确实被替换了,但是仅仅替换的是每行第一次出现的关键词,其他的却没有被替换,此时我们可以在s指令的末尾追加一个g标记,表示替换当前行匹配到的所有关键词。当然也允许我们追加一个具体的数字,表示将第几次出现的关键词进行替换操作。

# 将每行的所有localhost都替换成domain[root@localhost sed]# sed 's/localhost/mydomain/g' hosts127.0.0.1 mydomain mydomain.localdomain mydomain4 mydomain4.localdomain4::1 mydomain mydomain.localdomain mydomain6 mydomain6.localdomain6# 将最后一行的所有localhost都替换成domain[root@localhost sed]# sed '$s/localhost/mydomain/g' hosts127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4::1 mydomain mydomain.localdomain mydomain6 mydomain6.localdomain6# 将最后一行第3次出现的localhost替换成mydomain[root@localhost sed]# sed '$s/localhost/mydomain/3' hosts127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4::1 localhost localhost.localdomain mydomain6 localhost6.localdomain6

使用-n选项并在s指令后面追加p指令,则表示仅显示匹配的行

[root@localhost sed]# sed -n 's/localhost/LocalHost/gp' hosts127.0.0.1 LocalHost LocalHost.localdomain LocalHost4 LocalHost4.localdomain4::1 LocalHost LocalHost.localdomain LocalHost6 LocalHost6.localdomain6[root@localhost sed]#

在s指令的最后添加i标记,表示可以忽略大小写:

[root@localhost sed]# sed -i '$a add LocalHost data.' hosts[root@localhost sed]# cat hosts127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4::1 localhost localhost.localdomain localhost6 localhost6.localdomain6add LocalHost data.[root@localhost sed]# sed -n 's/localhost/aaa/gp' hosts127.0.0.1 aaa aaa.localdomain aaa4 aaa4.localdomain4::1 aaa aaa.localdomain aaa6 aaa6.localdomain6[root@localhost sed]# sed -n 's/LocalHost/bbb/gp' hostsadd bbb data.[root@localhost sed]# sed -n 's/LocalHost/ccc/ip' hosts127.0.0.1 ccc localhost.localdomain localhost4 localhost4.localdomain4::1 ccc localhost.localdomain localhost6 localhost6.localdomain6add ccc data.[root@localhost sed]#

使用s替换指令的同时添加e标记,则表示将替换后的内容当成shell命令在终端执行一次。

# 将字符串/etc/passwd通过管道传递给sed,使用^接收该字符串,然后将该字符串替换为ls -l /etc/passwd,替换后再终端执行替换后的命令。注意-l后面有空格。[root@localhost sed]# echo "/etc/passwd" | sed 's/^/ls -l /e'-rw-r--r--. 1 root root 929 Sep 12 15:26 /etc/passwd# 在/tmp/中touch一个名为tmpfile的空文件[root@localhost sed]# echo "tmpfile" | sed 's#^#touch /tmp/#e'[root@localhost sed]# ls /tmp/tmpfile/tmp/tmpfile[root@localhost sed]#

使用s指令替换时,如果要替换成的字符串为空,则表示将匹配到的关键词删除。

[root@localhost sed]# cat hosts127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4::1 localhost localhost.localdomain localhost6 localhost6.localdomain6add LocalHost data.# 删除每行第一次出现的localhost[root@localhost sed]# sed 's/localhost//' hosts127.0.0.1 localhost.localdomain localhost4 localhost4.localdomain4::1 localhost.localdomain localhost6 localhost6.localdomain6add LocalHost data.# 删除所有的localhost[root@localhost sed]# sed 's/localhost//g' hosts127.0.0.1 .localdomain 4 4.localdomain4::1 .localdomain 6 6.localdomain6add LocalHost data.# 删除所有得到localhost并且不区分大小写[root@localhost sed]# sed 's/localhost//gi' hosts127.0.0.1 .localdomain 4 4.localdomain4::1 .localdomain 6 6.localdomain6add data.[root@localhost sed]#

sed也可以使用正则表达式的保留功能来处理字符串:

#  (.{5})保留前五个字符,使用\1调用#  (.*)保留前后五个字符之间的所有字符,使用\2调用# (.{5})$保留最后五个字符,使用\3调用[root@localhost sed]# echo "Hello the World" | sed -r "s/(.{5})(.*)(.{5})$/\3\2\1/"World the Hello[root@localhost sed]# sed -r 's/(.{7})(.*)(.{8})/\3\2\1/' hostsldomain4.1 localhost localhost.localdomain localhost4 localhost4.loca127.0.0ldomain6 localhost localhost.localdomain localhost6 localhost6.loca::1st data.alHoadd Loc[root@localhost sed]#
-e选项的使用

使用sed时可以使用分号或者-e选项两种方式在一行中编写多条指令,可以直接使用分号将多个指令分隔,或者在多个-e选项后面添加sed指令,sed支持一个或多个-e选项。

# 打印出第1、第3、第5行[root@localhost sed]# sed -n '1p;3p;5p' /etc/shells/bin/sh/sbin/nologin/usr/bin/bash# 打印出第1、第3、第5行[root@localhost sed]# sed -n -e '1p' -e '3p' -e '5p' /etc/shells/bin/sh/sbin/nologin/usr/bin/bash# 将包含nologin的行中的sbin替换为bin,然后将每行的bin/删除[root@localhost sed]# sed '/nologin/s/sbin/bin/;s/bin\///' /etc/shells/sh/bash/nologin/usr/sh/usr/bash/usr/nologin[root@localhost sed]#

如果将分号放到花括号中还可以实现对指令进行分组。

# 仅替换和删除包含nologin的行中的bin/[root@localhost sed]# sed '/nologin/{s/sbin/bin/;s/bin\///}' /etc/shells/bin/sh/bin/bash/nologin/usr/bin/sh/usr/bin/bash/usr/nologin[root@localhost sed]#
-f选项的使用

有时候我们需要对文件增、删、改的操作比较多,虽然sed可以使用分号或-e选项在一行中分隔多个指令,但是当指令很多的时候并不是很方便,对于这种情况,我们可以先将所有的sed指令写入一个文本文件中,然后通过sed的-f选项读取这些指令即可实现多指令操作。

# 测试数据文件[root@localhost sed]# cat test.txthello the world.go squrs go.123 456 789.hello the beijing.I am Tony.# sed指令文件[root@localhost sed]# cat script.sed# 将第一行替换为 “hello world”1c hello world# 先打印第2行的内容,再将第2行的第一个g替换成G2{ps/g/G/}# 删除所有包含数字的行/[0-9]/d# 将包含beijing的行的第一个h替换成H并把改行的beijing替换成China/beijing/{s/h/H/s/beijing/China/}[root@localhost sed]#
# 执行sed命令[root@localhost sed]# sed -f script.sed test.txthello worldgo squrs go.Go squrs go.Hello the China.I am Tony.[root@localhost sed]#

标签: #centos74ssh升级