首页 > 技术文章 > sed的基础用法简介

qige2017 2017-08-12 20:58 原文

sed

最近学习了一些sed的相关知识,初步接触sed以后给我的感受主要有两点。首先是sed强大的功能,学了以后发现之前写的脚本利用sed以后会简化很多啊,具体的有些利用sed编辑shell脚本的思路我在下文中会有一定的体现。另外,一种工具功能强大就一定伴随的是知识点多。sed虽然是一个文本编辑工具但是他的命令用法总结起来可以说又是一门语言啦。所以关于sed的学习,我个人的意见有两点,一是慢慢持续的学,知识量大就慢慢学,一口吃个胖子不现实啊。但是一定要持续的去学习。二是多动手联系,sed支持正则表达式的用法,另外有些高级用法比较绕脑子,所以多动手去练着学效果会好很多。所以基于这点,这篇文章我首先会对sed的用法有所介绍,另外会引用相对多点的示例以及联系题。示例和练习题中包含的知识点还是比较有代表性的,另外一些关于使用sed的思路也会有所介绍。希望看完这篇文章以后会对你在学习sed这方面有所帮助。
sed英文叫Stream EDitor, 是一种行编辑器,关于sed的特点我主要总结了下面三点:

  1. sed是一种流编辑器,一次处理一行
  2. 每次处理一行,默认处理完成以后清除模式空间读入下一行,循环处理,知道文件末尾。
  3. 默认不改变源文件,使用重定向存储输出

功能:
自动编辑一个或者多个文件,简化对文件的反复操作,编写转换程序等。
用法:
sed [option]... 'script' inputfile...

常用选项:

-n:不输出模式空间内容到屏幕,即不自动打印
-e: 多点编辑
-f:/PATH/SCRIPT_FILE: 从指定文件中读取编辑脚本
-r: 支持使用扩展正则表达式
-i.bak: 备份文件并原处编辑
script:脚本由两部分组成,地址命令。

定界:

  1. 不定界,默认全文
  2. 单地址:#,指定行 /pattern/,被匹配到的每一行
  3. 地址范围
    第#行到第#行:#,#
    第#行之后的第#行:#,+#
    第一次匹配到的第二次匹配到的行:/pat1/,/pat2/
    第#行到第一次匹配到的行:#,/pat1/
  4. ~:步进
    1~2 奇数行:从第一行开始每隔两行执行命令一次
    2~2 偶数行:从第二行开始每隔两行执行命令一次

编辑命令:

d: 删除模式空间匹配的行,并立即启用下一轮循环
p:打印当前模式空间内容,追加到默认输出之后
a []text:在指定行后面追加文本 支持使用\n实现多行追加 行首有空格时加\转义。
i []text:在行前面插入文本
c []text:替换行为单行或多行文本
w /path/somefile: 保存模式匹配的行至指定文件
r /path/somefile:读取指定文件的文本至模式空间中 匹配到的行后
=: 为模式空间中的行打印行号
!:模式空间中匹配行取反处理
s///:查找替换,支持使用其它分隔符,s@@@,s###
替换标记:
g: 行内全局替换
p: 显示替换成功的行 :与-n配合只显示修改行
w /PATH/TO/SOMEFILE:将替换成功的行保存至文件中

示例:

  1. 将CentOS6.9的文件系统配置文件/etc/fstab中所有ext4文件系统改为xfs文件系统,并将修改结果保存到/app目录下的fstab文件中不打印修改结果:
[root@CentOS6 app]#sed -n 's/ext4/xfs/w /app/fstab' /etc/fstab 
# -n:不打印输出结果
# s/ext4/xfs/:搜索ext4替换为xfs
# w /app/fstab 保存修改结果到/app/fstab中
[root@CentOS6 app]#cat /app/fstab 
UUID=12caaaac-c7d5-4e4a-9e5e-2b234f8f977b /                       xfs    defaults        1 1
UUID=cf0be604-cbe3-4a1f-8970-c283dc8ab6f9 /app                    xfs    defaults        1 2
UUID=7493c7a2-3b38-4798-8a1b-e78ddc53fce1 /boot                   xfs    defaults        1 2
  1. 取出CentOS6.9中ifconfig eh0命令的ip地址:
[root@CentOS6 app]#ifconfig eth0 | sed -n -e '2s/.*r://' -e '2s/ .*$//p'
# -e:多点编辑
# -n:只显示匹配内容
# 2s/.*r:// :搜索第二行行首到r:之间任意字符替代为空//
# 2s/ .*$//p :搜索第二行空格到行尾之间任意字符代替为空//并打印
172.18.45.6
  1. 利用sed的后向引用用法,p2文件中行尾的bash替换成basher:
[root@CentOS6 ~]#sed -rn 's/(bash)$/\1er/p' p2
# -r:利用扩展正则表达式
# -n:静默打印
# s/(bash)$/\1er/p:搜索行尾的bash并分组,利用后向引用将bash替换然后打印
# sed具有单一后向引用写法,上边\1er写法可改为&er,不用分组
root:x:0:0:root:/root:/bin/basher
mysql:x:27:27:MySQL Server:/var/lib/mysql:/bin/basher
huxiaoqi:x:500:500::/home/huxiaoqi:/bin/basher
  1. 取出CentOS7中ifconfig ens33命令的ip地址:
[root@CentOS7 ~]#ifconfig ens33 | sed '2!d;s/.*et //;s/ n.*$//'
# 2!d:非第二行的删除
# s/.*et //:搜索任意字符到et 为止替代为空
# s/ n.*$//:搜索 n开始到行尾任意字符替代为空
# 利用分号“;”进行多次操作
172.18.45.7 

sed ‘2p’ /etc/passwd 第二行打印两次
sed –n ‘2p’ /etc/passwd 只显示第二行
sed –n ‘1,4p’ /etc/passwd 显示一到四行
sed –n ‘/root/p’ /etc/passwd 只显示带root的行
sed –n ‘2,/root/p’ /etc/passwd 显示第二行到包含root的行
sed -n ‘/^$/=’ file 显示空行行号
sed –n –e '/^$/p' –e '/^$/=' file 多点编辑打印空行显示行号
sed ‘/root/a\superman’ /etc/passwd 在包含root行后插入superman
sed ‘/root/i\superman’ /etc/passwd 在包含root行前插入superman
sed ‘/root/c\superman’ /etc/passwd 把包含root的行用superman替代

高级用法

P:打印模式空间开端至\n内容,并追加到默认输出之前
h: 把模式空间中的内容覆盖至保持空间中
H:把模式空间中的内容追加至保持空间中
g: 从保持空间取出数据覆盖至模式空间
G:从保持空间取出内容追加至模式空间
x: 把模式空间中的内容与保持空间中的内容进行互换
n: 读取匹配到的行的下一行覆盖至模式空间
N:读取匹配到的行的下一行追加至模式空间
d: 删除模式空间中的行
D:如果模式空间包含换行符,则删除直到第一个换行符的模 式空间中的文本,并不会读取新的输入行,而使用合成的模 式空间重新启动循环。如果模式空间不包含换行符,则会像 发出d命令那样启动正常的新循环
示例:
sed -n 'n;p' FILE
sed '1!G;h;$!d' FILE
sed 'N;D‘ FILE
sed '$!N;$!D' FILE
sed '$!d' FILE
sed ‘G’ FILE
sed ‘g’ FILE
sed ‘/^$/d;G’ FILE
sed 'n;d' FILE
sed -n '1!G;h;$p' FILE

练习:

1、删除centos7系统/etc/grub2.cfg文件中所有以空白开头 的行行首的空白字符

[root@CentOS7 ~]#sed -r 's/^[[:space:]]+//' /etc/grub2.cfg 
# -r:利用扩展正则表达式
# s/^[[:space:]]+//:搜索行首一个以上的空格以空替代
# +:表示前面匹配的内容有一个或一个以上
DO NOT EDIT THIS FILE
It is automatically generated by grub2-mkconfig using templates
from /etc/grub.d and settings from /etc/default/grub
BEGIN /etc/grub.d/00_header ###
set pager=1
if [ -s $prefix/grubenv ]; then
load_env
if [ "${next_entry}" ] ; then
set default="${next_entry}"
set next_entry=
...(内容有所简化)

2、删除/etc/fstab文件中所有以#开头,后面至少跟一个空 白字符的行的行首的#和空白字符

[root@CentOS7 ~]#sed -r 's/^#[[:space:]]+//' /etc/fstab 
# -r:利用扩展正则表达式
# s/^#[[:space:]]+//:搜索行首#开头跟至少一个空格,以空替代
/etc/fstab
Created by anaconda on Fri Jul 14 13:02:53 2017
Accessible filesystems, by reference, are maintained under '/dev/disk'
See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
UUID=851ee323-783c-44fe-93f5-333b9fe02b34 /                       xfs     defaults        0 0
UUID=44c6b90e-fe57-470c-ab8d-7349164d3a1c /app                    xfs     defaults        0 0
UUID=bcf325a3-2540-431c-b56f-9860bfca5efd /boot                   xfs     defaults        0 0
UUID=a2d5f98f-4a52-4d0d-902a-308b3bef3668 swap                    swap    defaults        0 0

3、在centos6系统/root/install.log每一行行首增加$号

[root@CentOS6 ~]#sed 's/^/$/' /root/install.log |less
# s/^/$/:搜索行首加$
# less:分页查看
$Installing libgcc-4.4.7-18.el6.x86_64
$warning: libgcc-4.4.7-18.el6.x86_64: Header V3 RSA/SHA1 Signature, key ID c105b9de: NOKEY
$Installing fontpackages-filesystem-1.41-1.1.el6.noarch
$Installing m17n-db-1.5.5-1.1.el6.noarch
$Installing liberation-fonts-common-1.05.1.20090721-5.el6.noarch
$Installing setup-2.8.14-23.el6.noarch
...

4、在/etc/fstab文件中不以#开头的行的行首增加#号

[root@CentOS7 ~]#sed '/^#/!s/^/#/'  /etc/fstab 
# /^#/!:定界非#开头的行
# s/^/#/:搜索行首加#号
# 作用相当于注释掉了/etc/fstab配置文件中的所有内容
# /etc/fstab
# Created by anaconda on Fri Jul 14 13:02:53 2017
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more 
#UUID=851ee323-783c-44fe-93f5-333b9fe02b34 /                       xfs     defaults        0 0
#UUID=44c6b90e-fe57-470c-ab8d-7349164d3a1c /app                    xfs     defaults        0 0
#UUID=bcf325a3-2540-431c-b56f-9860bfca5efd /boot                   xfs     defaults        0 0
#UUID=a2d5f98f-4a52-4d0d-902a-308b3bef3668 swap                    swap    defaults        0 0

5、处理/etc/sysconfig/network-scripts/路径,使用sed命令取出其目录名和基名

[root@CentOS7 ~]#echo /etc/sysconfig/network-scripts/ | sed -r 's/^(.*\/)([^/]+\/?)/\1/'
# ^(.*\/):行首任意字符到/并用()分组
# ([^/]+\/?):非/的任意一个以上字符到/可有可无并分组
# ^(.*\/)([^/]+\/?):代表全文分为两组
# s匹配搜索全文以\1第一组替换就表示目录名,以\2第二组替代就表示基名
/etc/sysconfig/
[root@CentOS7 ~]#echo /etc/sysconfig/network-scripts/ | sed -r 's/^(.*\/)([^/]+\/?)/\2/'
network-scripts/

6、统计centos安装光盘中Package目录下的所有rpm文件的 以.分隔倒数第二个字段的重复次数

[root@CentOS7 ~]#ls /run/media/root/CentOS\ 7\ x86_64/Packages/ |sed -r 's/.rpm$//;s/.*\.//'  |sort  |uniq -c |sort -n
# s/.rpm$//:搜索结尾的.rpm以空代替
# s/.*\.//:搜索任意字符到点(点需要转义),以空代替
      1 TBL
   2072 i686
   3044 noarch
   4247 x86_64

7、统计/etc/init.d/functions文件中每个单词的出现次数, 并排序(用grep和sed两种方法分别实现)

[root@CentOS7 ~]#egrep -o "[[:alpha:]]+" /etc/init.d/functions |sort |uniq -c |sort -nr |less
     67 pid
     54 if
     54 file
     52 echo
     50 return
     46 then
     45 fi
[root@CentOS7 ~]#sed -r 's/[^[:alpha:]]/\n/g' /etc/init.d/functions |sed  '/^$/d'|sort|uniq -c |sort -nr |less
# 思路:因为sed是一个行处理器,他不像grep可以将单个单词提取出来,所以考虑将文本中任意字母以外的任意字符都用\n换行符替代进而使用sed逐行处理。
     67 pid
     54 if
     54 file
     52 echo
     50 return
     46 then
     45 fi

看完以后对你有没有帮助呢?请留言交流吧,另外如果有错误和不足之处请多指教。

推荐阅读