sed 介绍
sed 是一个非交互式流编辑器,默认它并不修改原始文件,除非指定了 -i 或者 -i.bak 选项,前者直接修改原文件,不自动做备份,后者将原文件备份一份.bak 文件后,直接修改原文件。
sed 语法
sed [选项] '[指定要操作的行]{sed命令}' filename
命令可以用 {} 括起来
指定要操作的行
如果未指定要操作的行,则操作所有行。
可以通过行号或者正则来定位到行。
定位行的方式 | 含义 |
---|---|
2 | 对第2行进行操作 |
2,10 | 对第2行到第10行进行操作 |
2,2+3 | 对第2行到第5(2+3)行进行操作 |
1~2 | 对1,3,5,7,9....行进行操作 |
2,$ | 对第2行到最后一行进行操作 |
/cfop/ | 对匹配 cfop 的行进行操作 |
/cfop/,/cy/ | 对匹配 cfop 的行到匹配 cy 的行进行操作 |
/cfop/,5 | 对匹配到 cfop 的行到第5行进行操作 |
5,/cfop/ | 对第5行到匹配到 cfop 的行进行操作 |
/cfop/,+2 | 对匹配到 cfop 的行及其后的2行进行操作 |
2,10! | 不对2到10行进行操作 |
sed 命令之 p 打印
# sed -n '/apache/,+5{p}' /etc/passwd
sed 命令之 = 显示行号
# sed -n '/apache/=' /etc/passwd
sed 命令之 c 替换整行
# sed '/apache/c\httpd web' /etc/passwd
注意这儿是把整行替换为 httpd web,如果只想替换部分内容,使用 s 命令。
sed 命令之 d 删除行
# sed '/apache/d' /etc/passwd 删除包含关键字的行
# sed '/apache/d' /etc/passwd 删除不包含关键字的行
sed 命令之 a/i 增加行
a在指定行后面增加,i在指定行前面增加
# sed '/apache/i GOODLUCK' /etc/passwd
如果想添加多行,在行与行之间使用 n
# sed '/apache/i GOODLUCK\nMAN' /etc/passwd
sed 命令之 w 写入文件
# sed '/apache/w /tmp/sed.tmp' /etc/passwd
该命令会将包含 apache 的行写入到 /tmp/sed.tmp 文件中
sed 命令之 r 读入文件
# sed '/apache/r /tmp/sed.tmp' /etc/passwd
将 /tmp/sed.tmp 文件中的内容读入到包含 apache 的行的后面,结合上一个示例,将出现两个包含 apache 的行
sed 命令之 s 替换
s/替换谁/用什么替换/[gpw]
第一个部分的模式支持正则表达式,后面的模式不支持。这儿的 / 也可以替换成其它符号,一般使用 / 或 #,当匹配字符串中也有 / 时,使用 # 比较方便。例如:
# sed 's#/sbin/nologin#cfop#g' /etc/passwd
这儿的意思是将 passwd 文件中所有的 /sbin/nologin 替换成 cfop,使用 # 就避免了对字符串中的 / 进行转义。
gpw 的解释:
g 缺省只替换每行的第一次出现模式,使用 g 替换所有模式。
p 仅将所有被替换行输出到标准输出。对于 s 命令,只有 -n 选项是不行的。
w 仅将所有被替换行输出重定向到文件(直接加 w 后面跟上文件名即可,用空格隔开)
使用s命令时包含变量的注意事项:
# cat test.txt
a b a
# x=a;y=b
# sed 's/$x/$y/g' test.txt <-- 使用单引号时,变量不做替换
a b a
# sed ''s/$x/$y/g'' test.txt <-- 将单引号改成双引号(或者不要引号)就可以了
b b b
# eval sed 's/$x/$y/g' test.txt <-- 也可以使用eval命令把单引号里的变量进行计算
b b b
使用 & 保存发现模式:
使用 & 可以保存发现模式,这样就可以再次调用它,特别适用于发现模式使用了正则表达式的时候。
# cat demo.txt
The local nurse Miss P.Neave was in attendance.
# sed -n 's/nurse/iamrhce &/p' demo.txt <-- & 就是前面匹配模式的正则内容
The local iamrhce nurse Miss P.Neave was in attendance.
sed 命令之 s 后向引用
sed -nr ‘s#()()#\1\2#gp’ file
当在前面匹配部分用小括号的时候,第1个括号内容,可以在后面部分用1输出。第2个括号内容,可以在后面部分用2输出。
例1:把 'I am RHCA xuwang' 中的RHCA取出来
# echo 'I am RHCA xuwang' | sed -nr 's#^.*m (.*) x.*$#\1#gp'
例2:取出 oracle trace 目录的路径
通过 sql 语句查询到 trace 文件所在的目录,spool 到一个文件中,下面抓取其路径:
$ grep trace /tmp/check_trace.txt
/data/app/oracle/diag/rdbms/crdb/CYDB1/trace
我们看到 trace 目录后面有很多空白,这是因为 $ 结束符在后面的原因。
如果想取出文件中的路径,可以使用 awk 打印 $1,也可以使用下面的命令:
# sed -nr 's#(^.*trace).*$#\1#gp' /tmp/check_trace.txt
例3:取出 DB2 配置文件中的 key-value
有文件out.txt,内容如下,希望将等号左边第1个括号和等号右边的内容取出来:
Database Manager Configuration
Node type = Enterprise Server Edition with local and remote clients
Database manager configuration release level = 0x1000
CPU speed (millisec/instruction) CPUSPEED) = 2.086187e-07
Communications bandwidth (MB/sec) (COMM_BANDWIDTH) = 1.000000e+02
Max number of concurrently active databases (NUMDB) = 32
Current effective arch level (CUR_EFF_ARCH_LVL) = V:10 R:5 M:0 F:3 I:0 SB:0
解法:
$ sed -nr 's#^.*(\(.*\) =.*$)#\1#gp' out.txt
(CPUSPEED) = 2.086187e-07
(COMM_BANDWIDTH) = 1.000000e+02
(NUMDB) = 32
(CUR_EFF_ARCH_LVL) = V:10 R:5 M:0 F:3 I:0 SB:0
例4:将 passwd 文件中的第1列与最后一列互换
# sed -nr 's#([^:]+)(:.*:)(/.*$)#\3\2\1#gp' /etc/passwd
([^:]+)表示不是冒号重复1次或多次,即用户名,(:.*:)表示中间的部分,(/.*$)表示最后一列。
同时执行多个 sed 命令
如果想在一条 sed shell 命令中同时执行多个 sed 命令,可以使用 -e 选项来指定每个命令。
例如,可以使用 -e 将多个 s 命令拼接起来,同时完成多个替换:
sed -e 's/Installing/install/g' -e 's/i686/x86_64/g' install.log
可以使用 -e 将多个不同的 sed 命令拼接起来:
# sed -n -e '5s#/sbin/nologin#cfop#gp' -e '5a Goodluck' /etc/passwd
sed 的模式空间和保持空间
这是一个高级话题,我们另开一章,见文。