【数据处理】sed原理及使用举例(快速理解核心)

2022-03-30 14:07:06 浏览数 (1)

在做数据开发中,经常需要通过shell脚本/命令来针对文本进行预处理,sed是一个很强大的流式处理命令,笔者几乎每天都会用到,在这统一梳理总结了下! 其实 sed 很简单,比vim简单很多了!

1. 基础

核心概念

  • 两个空间: 模式空间(pattern space); 交换空间(hold space 保持空间)
    • 模式空间:容纳当前行的缓冲区,即通过模式匹配到的行被读入该空间中
    • 保持空间:一个辅助缓冲区,可以和模式空间进行交互(通过h,H,g,G),但命令不能直接作用于该空间,在进行数据处理时作为“暂存区域”
原理概念原理概念
  • 执行步骤: 1)读入一行数据到模式空间 2)在模式空间执行sed命令 3)将更新/修改后的内容输出 4)清空模式空间,并重复第一步,直到文件结束 执行流程执行流程

联想记忆: 模式对应G(在左边), 交换对应H(在右边)

  • 两种执行方式:
    • 一般常用:sed [options] 'command' file(s)
    • 脚本文件:sed [options] -f scriptfile file(s)
  • 查看帮助:man sed
  • 官方查询文档:TLDP--The Linux Document Project

参数

  • sed最后会输出模式空间的所有内容(除非指定了-n参数)
  • 变量传递通过-v参数(建议)
  • -e: 支持多个编辑命令
    • 命令的执行顺序对结果有影响
    • (-e)选项允许在同一行里执行多条命令。如例子所示,第一条命令删除1至5行,第二条命令用check替换test。命令的执行顺序对结果有影响。如果两个命令都是替换命令,那么第一个替换命令将影响第二个替换命令的结果。
      • sed -e '1,5d' -e 's/test/check/' example
代码语言:txt复制
- 一个比-e更好的命令是`--expression`。它能给sed表达式赋值。  
`sed --expression='s/test/check/' --expression='/love/d' example`

正则匹配(元字符集)

代码语言:shell复制
^
#锚定行的开始 如:/^sed/匹配所有以sed开头的行。   
$  
#锚定行的结束 如:/sed$/匹配所有以sed结尾的行。   
.  
#匹配一个非换行符的字符 如:/s.d/匹配s后接一个任意字符,然后是d。   
*  
#匹配零或多个字符 如:/*sed/匹配所有模板是一个或多个空格后紧跟sed的行。  
[] 
#匹配一个指定范围内的字符,如/[Ss]ed/匹配sed和Sed。  
[^] 
#匹配一个不在指定范围内的字符,如:/[^A-RT-Z]ed/匹配不包含A-R和T-Z的一个字母开头,紧跟ed的行。  
(..) 
#保存匹配的字符,如s/(love)able/1rs,loveable被替换成lovers。  
& 
#保存搜索字符用来替换其他字符,如s/love/**&**/,love这成**love**。   
<  
#锚定单词的开始,如:/<love/匹配包含以love开头的单词的行。   
>  
#锚定单词的结束,如/love>/匹配包含以love结尾的单词的行。   
x{m}  
#重复字符x,m次,如:/0{5}/匹配包含5个o的行。   
x{m,}  
#重复字符x,至少m次,如:/o{5,}/匹配至少有5个o的行。   
x{m,n}  
#重复字符x,至少m次,不多于n次,如:/o{5,10}/匹配5--10个o的行。  

2. 常用命令

s 替换文本(匹配)

代码语言:shell复制
#在整行范围内把test替换为mytest。如果没有g标记,则只有每行第一个匹配的test被替换成mytest。
$ sed 's/test/mytest/g' example 

#s选项和p标志一起使用表示只打印那些发生替换的行。也就是说,如果某一行开头的test被替换成mytest,就打印它。 
$ sed -n 's/^test/mytest/p' example   

#&符号表示替换换字符串中被找到的部份。所有以192.168.0.1开头的行都会被替换成它自已加 localhost,变成192.168.0.1localhost。 
 $ sed 's/^192.168.0.1/&localhost/' example  

#love被标记为1,所有loveable会被替换成lovers,而且替换的行会被打印出来。  
 $ sed -n 's/(love)able/1rs/p' example

#不论什么字符,紧跟着s命令的都被认为是新的分隔符,所以,“#”在这里是分隔符,代替了默认的“/”分隔符。表示把所有10替换成100。
$ sed 's#10#100#g' examplex   

#选定行的范围:逗号  
#所有在模板test和check所确定的范围内的行都被打印。 
 $ sed -n '/test/,/check/p' example   

#打印从第五行开始到第一个包含以test开始的行之间的所有行。
 $ sed -n '5,/^test/p' example  

#对于模板test和west之间的行,每行的末尾用字符串sed test替换。   
 $ sed '/test/,/check/s/$/sed test/' example 


#如果test被匹配,则移动到匹配行的下一行,替换这一行的aa,变为bb,并打印该行,然后继续。
$ sed '/test/{ n; s/aa/bb/; }' example 

# 替换匹配到的文本
sed -i "s|^(.*)#(SSLCertificateChainFile).*|12 /etc/apache2/ssl/1_root_bundle.crt|g" default-ssl.conf

# 替换匹配到的文本
sed -i "s download.docker.com ${DOCKER_RE_REPO} " /etc/yum.repos.d/docker-ce.repo

# 匹配再替换
sed -i "/^baseurl=http:/ { s,http://.*/hdp,http://${YUM_SERVER_IP}/hdp, } " hdp-utils.repo

# 匹配
# 如果test被匹配,则移动到匹配行的下一行,替换这一行的aa,变为bb,并打印该行,然后继续。           
sed '/test/{ n; s/aa/bb/; }' example

# 和;都得转义
# 匹配slash无法用其他字符替换
# a:在匹配行的下一行追加, 可以不加,但加上表示后面为追加内容,且表示空格也能插入       
sed -i "/sinclude /etc/nginx/conf.d/*.conf;/a    include /deploy/nginx/*.conf;" /etc/nginx/nginx.conf.bak

a,c,i,q,r,=,#,y

a: 在当前行后面加入一行文本 i: 在当前行上面插入文本 c: 用新的文本改变本行的文本 q: 退出Sed r: 从file中读行 w: 表示把行写入一个文件 (W: 追加) =: 打印当前行号码 #: 把注释扩展到下一个换行符以前。 y: 一个字符翻译为另外的字符(但是不用于正则表达式)

  • 用法举例
代码语言:shell复制
# 从文件读入:r命令  
# file里的内容被读进来,显示在与test匹配的行后面,如果匹配多行,则file的内容将显示在所有匹配行的下面。  sed '/test/r file' example  

# 写入文件:w命令  
#在example中所有包含test的行都被写入file里。   
sed -n '/test/w file' example 

# 追加命令:a命令
# this is a example'被追加到以test开头的行后面,sed要求命令a后面有一个反斜杠。  
sed '/^test/a\--->this is a example' example 
 
# 插入:i命令
# 如果test被匹配,则把反斜杠后面的文本插入到匹配行的前面。  
sed '/test/i\  
 new line  
 -------------------------' example  
  
#变形:y命令  
#把1--10行内所有abcde转变为大写,注意,正则表达式元字符不能使用这个命令。   
sed '1,10y/abcde/ABCDE/' example 

#退出:q命令
#打印完第10行后,退出sed
sed '10q' example 。 

n,N

模式空间 n: 下一行 ,模式空间内容被覆盖 N: 将当前行和下一行(中间n保留),一起追加到模式空间

  • N: 追加下一个输入行到模板块后面并在二者间嵌入一个新行,改变当前行号码。
代码语言:shell复制
[root@VM_0_6_centos ~]# seq 6 | sed -n '1{n;p}'
2
seq 6 | sed -n '1{N;p}'
1
2
[root@VM_0_6_centos ~]# seq 6 | sed -n '2{N;p}'
2
3
[root@VM_0_6_centos ~]# seq 6 | sed -n '2{n;p}'
3

h,H,p,P,x

交换空间 h: 用模式空间内容覆盖交换空间 H: 将模式空间内容追加到交换空间 p/P: 模式空间输出 x: 交换空间&模式空间内容交换 g: 将交换空间的内容,覆盖到模式空间 G: 将交换空间的内容,追加到模式空间

  • 互换模式空间和保持缓冲区的内容。也就是把包含test与check的行互换
  • sed -e '/test/h' -e '/check/x' example
  • 例子
代码语言:shell复制
[root@VM_0_6_centos ~]# seq 6 | sed '1h;3g'
1
2
1
4
5
6
[root@VM_0_6_centos ~]# seq 6 | sed '1h;3G'
1
2
3
1
4
5
6
  • 复杂的例子:
    • sed -n '/CST/h;/syncing/{x;//!p;g;P}
      • //!:简写,拿到最近的匹配结果,!表示若匹配不到

D,d

模式空间,删除 模式空间没有回车符,D/d一样 D/d执行后,都会跳到下一行(不管模式空间是否有内容)

  • 简单用法
代码语言:shell复制
#删除第N行
sed -i 'Nd' filename
#删除第N~M行
sed -i 'N,Md' filename # file的[N,M]行都被删除
#删除shell变量表示的行号(配合for等语句使用)
sed -i "${var1},${var2}d" filename # 这里引号必须为双引号
#删除最后一行
sed -i '$d' filename
  • 打印带有hello段落(段和段之间用空隔分开)
    • d后面的操作不执行(直接跳到下一行)
    • 比较复杂,直接忽略: sed '/./{H;d;x;/hello/!d
  • awk实现:打印带有hello的段落
    • awk -v RS=' ' "/hello/
代码语言:shell复制
cat >test.txt <<EOF
1111
2222
  3333hello
4444
  5555
6666
EOF

awk -v RS=' ' "/hello/" test.txt
3333hello
4444
  • 删除文件最后五行内容
    • 维持一个队列: 2-5行循环执行a,即N操作
    • 第6行的时候,打印模式空间中的第1行(P),并删除第1行(D)
    • 最后一行的时候,把模式空间清空($d
    • seq 7 | sed ':a;$d;N;2,5ba;P;D'
  • 波浪线用法a~b:a起始点,b间隔
  • 每隔两行输出一行
    • seq 10 | sed -n '1~3p'
      • 1~3: 1-从什么时候开始 ,3表示间隔3行
    • seq 10 | sed -n '0~3p'
代码语言:shell复制
seq 10 | awk 'NR%3==1'
1
4
7
10

addr1,addr2用法

依次匹配addr1, addr2,匹配到即进行操作 addr1:匹配到后执行(开关打开),如果当前行>addr1

addr2:匹配到后执行(开关关闭)

  • 准确的理解seq 5 | sed '1,2d;1,2d'
    • 输出的应该是第4和第5行,因为第二行删除后,第一个1,2d关闭
    • 再读入第3行时,匹配到第2个1,2d,因为3>1,所以匹配成功,执行d删除第3行
    • 继续读入第4行后,发现2<4,匹配不成功,关闭,所以第4行不做d操作
    • 接着第5行后所有内容因为命令全部关闭,直接输出
代码语言:shell复制
cat >test.txt <<EOF
a1
a2
a3
EOF

# 输出1-3行
sed '1,3{s/a/ax/}' test.txt
ax1
ax2
ax3

# 第一行就匹配到了a
sed '0,/a/{s/a/ax/}' test.txt
ax1
a2
a3

# 第一行匹配到1, 第二行匹配到a
sed '1,/a/{s/a/ax/}' test.txt
ax1
ax2
a3

# 一直匹配不到x
sed '1,/x/{s/a/ax/}' test.txt
ax1
ax2
ax3

t,b标签:

多用于做循环 t: 没有匹配到时跳转到label,无label则跳转到结尾

b: 调准到特定的label

  • :a: 定义label a
  • 把第偶数出现的1,改为0
代码语言:shell复制
cat >test.txt <<EOF
1
2
1
3
1
1
EOF

sed '/1/{:a;n;s/1/0/;tb;ba;:b}' test.txt
1
2
0
3
1
0

3. 简单自测

  • G,H作用,以及和g,h区别
    • G,H: 追加,G (交换空间->模式空间) H(模式空间->交换空间)
    • g,h: 覆盖
  • x作用
    • 模式空间和交换空间交换
  • echo abc | sed 's/.*/&n&/'
    • &即引用前面匹配的结果
  • sed每隔10行输出一个
  • seq 5 | sed -n 'x;p'
  • seq 5 | sed -n 'x;p;x'
  • sed '$!N;$!D'sed 'N;D', sed '$!D;D'

4. 组合应用

  • find, sed:同时替换多个文件内的服务名
代码语言:shell复制
find . -type f -print0 | xargs -0 sed -i "s/<SERVER_NAME>/${SVR_NAME}/g" 

0 人点赞