Linux基础命令介绍十:文本流编辑 sed

系统 Linux
sed的工作过程是这样的:首先,初始化两个数据缓冲区模式空间和保持空间;sed读取一行输入(来自标准输入或文件),去掉结尾的换行符(\n)后置于模式空间中,然后针对模式空间中的字符串开始执行‘sed命令’,每个命令都可以有地址与之相关联,地址可以看成是条件,只有在条件成立时,相关的命令才被执行;所有可执行命令都处理完毕后,仍处于模式空间中的字符串会被追加一个换行符后打印输出;之后读取下一行输入做同样的处理,直到主动退出(q)或输入结束。

[[179651]]

与vim不同,sed是一种非交互式的文本编辑器,同时它又是面向字符流的,每行数据经过sed处理后输出。 

  1. sed [OPTION]... [script] [file]... 

sed的工作过程是这样的:首先,初始化两个数据缓冲区模式空间和保持空间;sed读取一行输入(来自标准输入或文件),去掉结尾的换行符(\n)后置于模式空间中,然后针对模式空间中的字符串开始执行‘sed命令’,每个命令都可以有地址与之相关联,地址可以看成是条件,只有在条件成立时,相关的命令才被执行;所有可执行命令都处理完毕后,仍处于模式空间中的字符串会被追加一个换行符后打印输出;之后读取下一行输入做同样的处理,直到主动退出(q)或输入结束。

地址

地址可以是如下的形式

1、number 表示行号

2、first~step 表示从first(数字)行开始,每隔step(数字)行

3、$ 表示***一行(注意当出现在正则表达式中时表示行尾)

4、/regexp/ 表示匹配正则表达式regexp(关于正则表达式,请参见这一篇)

5、\%regexp% 表示匹配正则表达式regexp,%可以换成任意其他单个字符。(用于regexp包含斜线/的情况)

6、/regexp/I 匹配正则表达式regexp时不区分大小写

7、/regexp/M 启用正则多行模式,使$不止匹配行尾,还匹配n或r之前的位置;使^不止匹配行首,还匹配n或r之后的位置。此时可以用(\`)匹配模式空间的开头位置,用(\')匹配模式空间的结束位置。

还可以用逗号,分隔两个地址来表示一个范围

表示从匹配***个地址开始,直到匹配第二个地址或文件结尾为止。如果第二个地址是个正则表达式,则不会对***个地址匹配行进行第二个地址的匹配;如果第二个地址是行号,但小于或等于***个地址匹配行行号,则只会匹配一行(***个地址匹配行)。

8、0,/regexp/ 这种情况下,正则表达式regexp会在***行就开始进行匹配。只有第二个地址是正则表达式时,***个地址才能用0。

9、addr1,+n表示匹配地址addr1和其后的n行。

10、addr1,~n表示从匹配地址addr1开始,直到n的倍数行为止。

如果没有给出地址,所有的行都会匹配;在地址或地址范围后追加字符!表示对地址取反,所有不匹配的行才会被处理。

选项

-n 默认时每一行处理过的字符串都会被打印输出,此选项表示关闭此默认行为。只有被命令p作用的字符串才会被输出。

-f file表示从file中读取sed命令

-i 表示原地修改。应用此选项时,sed会创建一个临时文件,并将处理结果输出到此文件,处理完毕后,会将此临时文件覆盖至原文件。

-r 表示使用扩展的正则表达式

命令

p表示打印模式空间内容,通常配合选项-n一起使用

  1. [root@centos7 ~]# seq 5 
  2. [root@centos7 ~]# 只输出第二行到第四行 
  3. [root@centos7 ~]# seq 5|sed -n '2,4p' 
  4. [root@centos7 ~]#   

d 删除模式空间内容,立即处理下一行输入。

  1. #删除***一行 
  2. [root@centos7 ~]# seq 5|sed '$d' 
  3. [root@centos7 ~]#  

q 立即退出,不再处理任何命令和输入(只接受单个地址)

  1. [root@centos7 ~]# seq 5|sed '/3/q' 
  2. [root@centos7 ~]#  

n 如果没有使用选项-n,输出模式空间中内容后,读取下一行输入并覆盖当前模式空间内容。如果没有更多的输入行,sed会退出执行。

  1. [root@centos7 ~]# seq 9|sed -n 'n;p' 
  2. [root@centos7 ~]# 注意多个命令用分号分隔  

s/regexp/replacement/flag 表示用replacement替换模式空间中匹配正则表达式regexp的部分。在这里符号/可以换成任意单个字符。 

  1. [root@centos7 ~]# echo "hello123world"|sed 's/[0-9]\+/,/'   
  2. hello,world 
  3. #注意这里+需要转义,如果使用选项-r则无需转义  

在replacement中

1、\n (n为1-9中的一个数字)表示对正则表达式中分组(...)的引用;

  1. [root@centos7 ~]# echo "hello123world"|sed -r 's/[a-z]+([0-9]+)[a-z]+/\1/' 
  2. 123 
  3. [root@centos7 ~]# echo "hello123world"|sed -r 's/([a-z]+)[0-9]+([a-z]+)/\1,\2/' 
  4. hello,world  

2、&表示模式空间中所有匹配regexp的部分;

  1. [root@centos7 ~]# echo "hello123world"|sed -r 's/[0-9]+/:&:/' 
  2. hello:123:world  

3、\L 将后面的字符转化成小写直到 \U 或 \E 出现;

4、\l 将下一个字符转化为小写;

5、\U 将后面的字符转化成大写直到 \L 或 \E 出现;

6、\u 将下一个字符转化为大写;

7、\E 停止由 \L 或 \U 起始的大小写转化;

  1. [root@centos7 ~]# echo "hello123world"|sed -r 's/^([a-z]+)[0-9]+([a-z]+)$/\U\1\E,\u\2/' 
  2. HELLO,World 
  3. [root@centos7 ~]#   

flag

1、n数字n表示替换第n个匹配项

  1. [root@centos7 ~]# head -1 /etc/passwd 
  2. root:x:0:0:root:/root:/bin/bash 
  3. #替换冒号分隔的第五部分为空 
  4. [root@centos7 ~]# head -1 /etc/passwd|sed 's/[^:]\+://5' 
  5. root:x:0:0:/root:/bin/bash  

2、g表示全局替换

  1. [root@centos7 ~]# echo "hello123world"|sed 's/./\U&\E/' 
  2. Hello123world 
  3. [root@centos7 ~]#  
  4. [root@centos7 ~]# echo "hello123world"|sed 's/./\U&\E/g' 
  5. HELLO123WORLD 
  6. [root@centos7 ~]# 
  7. #当数字n和g同时使用时,表示从第n个匹配项开始替换一直到***匹配项 
  8. [root@centos7 ~]# head -1 /etc/passwd|sed 's/[^:]\+://4g' 
  9. root:x:0:/bin/bash/  

3、p表示如果替换成功,则打印模式空间内容。

4、w file表示如果替换成功,则输出模式空间内容至文件file中。

5、I和i表示匹配regexp时不区分大小写。

  1. [root@centos7 ~]# echo 'HELLO123world'|sed -r 's/[a-z]+//Ig' 
  2. 123 
  3. [root@centos7 ~]#  

6、M和m表示启用正则多行模式(如前所述)。(讲命令N时再举例)

  1. [root@centos7 ~]# echo hello|sed 'y/el/LE/'       
  2. hLEEo 
  3. [root@centos7 ~]#  

a text表示输出模式空间内容后追加输出text内容

  1. [root@centos7 ~]# seq 3|sed '1,2a hello'  
  2. hello 
  3. hello 
  4. [root@centos7 ~]#  

i text表示输出模式空间内容之前,先输出text内容

  1. [root@centos7 ~]# seq 3|sed '$ihello' 
  2. hello 
  3. [root@centos7 ~]#   

c text表示删除匹配地址或地址范围的模式空间内容,输出text内容。如果是单地址,则每个匹配行都输出,如果是地址范围,则只输出一次。

  1. [root@centos7 ~]# seq 5|sed '1,3chello' 
  2. hello 
  3. [root@centos7 ~]# seq 5|sed '/^[^3-4]/c hello'  
  4. hello 
  5. hello 
  6. hello  

=表示打印当前输入行行号

  1. [root@centos7 ~]# seq 100|sed -n '$=' 
  2. 100 
  3. [root@centos7 ~]# seq 100|sed -n '/^10\|^20/=' 
  4. 10 
  5. 20 
  6. 100 
  7. [root@centos7 ~]# 转义的|表示逻辑或  

r file表示读取file的内容,并在当前模式空间内容输出之后输出

  1. [root@centos7 ~]# cat file  
  2. hello world 
  3. [root@centos7 ~]# seq 3|sed '1,2r file' 
  4. hello world 
  5. hello world 
  6. [root@centos7 ~]#   

w file表示输出模式空间内容至file中

N读入一行内容至模式空间后,再追加下一行内容至模式空间(此时模式空间中内容形如 line1\nline2 ),如果不存在下一行,sed会退出。

  1. [root@centos7 ~]# seq 10|sed -n 'N;s/\n/ /p' 
  2. 1 2 
  3. 3 4 
  4. 5 6 
  5. 7 8 
  6. 9 10 
  7. [root@centos7 ~]# 
  8. #s命令的m flag举例 
  9. [root@centos7 ~]# seq 3|sed 'N;s/^2/xxx/'  
  10. [root@centos7 ~]# seq 3|sed 'N;s/^2/xxx/m'     
  11. xxx 
  12. [root@centos7 ~]# seq 3|sed 'N;s/1$/xxx/'  
  13. [root@centos7 ~]# seq 3|sed 'N;s/1$/xxx/M' 
  14. xxx 
  15.  

D如果模式空间中没有新行(如命令N产生的新行),则和命令d起同样作用;如果包含新行,则会删除***行内容,然后对模式空间中剩余内容重新开始一轮处理。(注意:D后面的命令将会被忽略)

  1. [root@centos7 ~]# seq 5|sed 'N;D'   
  2. [root@centos7 ~]# seq 5|sed 'N;N;D'    
  3.  

P打印模式空间中***行内容

  1. [root@centos7 ~]# seq 10|sed -n 'N;P'  
  2. [root@centos7 ~]# seq 10|sed -n 'N;N;P' 
  3. #注意另一种写法输出中的不同 
  4. [root@centos7 ~]# seq 10|sed -n '1~3P'  
  5. 10  

g用保持空间中的内容替换模式空间中的内容

  1. [root@centos7 ~]# seq 5|sed -n 'g;N;s/\n/xx/p' 
  2. xx2 
  3. xx4 
  4. [root@centos7 ~]#   

G追加一个换行符到模式空间,然后再将保持空间中的内容追加至换行符之后。(此时模式空间中内容形如 PATTERN\nHOLD )

  1. [root@centos7 ~]# seq 5|sed 'G;s/\n/xx/'   
  2. 1xx 
  3. 2xx 
  4. 3xx 
  5. 4xx 
  6. 5xx  

h用模式空间中的内容替换保持空间中的内容(注意此时模式空间中的内容并没有被清除)

  1. [root@centos7 ~]# seq 5|sed -n 'h;G;s/\n/xx/p' 
  2. 1xx1 
  3. 2xx2 
  4. 3xx3 
  5. 4xx4 
  6. 5xx5 
  7. [root@centos7 ~]# seq 5|sed -n 'h;G;G;s/\n/xx/gp' 
  8. 1xx1xx1 
  9. 2xx2xx2 
  10. 3xx3xx3 
  11. 4xx4xx4 
  12. 5xx5xx5  

H追加一个换行符到保持空间,然后再将模式空间中的内容追加至换行符之后。(此时保持空间中内容形如 HOLD\nPATTERN )

  1. [root@centos7 ~]# seq 3|sed -n 'H;G;s/\n/xx/gp' 
  2. 1xxxx1 
  3. 2xxxx1xx2 
  4. 3xxxx1xx2xx3 
  5. [root@centos7 ~]#   

x交换模式空间和保持空间的内容

  1. [root@centos7 ~]# seq 9|sed -n '1!{x;N};s/\n//p' 
  2. 25 
  3. 47 
  4. 69 
  5. #处于{...}之中的是命令组  

: label为分支命令指定标签位置(不允许地址匹配)

b label无条件跳转到label分支,如果省略了label,则跳转到整条命令结尾(即开始下一次读入) 

  1. #如删除xml文件中注释部分(<!--...-->之间的部分是注释,可以多行) 
  2. sed '/<!--/{:a;/-->/!{N;ba};d}' server.xml 
  3. #表示匹配<!--开始,在匹配到-->之前一直执行N,匹配到-->之后删除模式空间中内容 
  4. #如在nagios的配置文件中,有许多define host{...}的字段,如下所示: 
  5. define host{ 
  6. use windows-server 
  7. host_name serverA 
  8. hostgroups 060202 
  9. alias 060202 
  10. contact_groups yu 
  11. address 192.168.1.1 
  12. #现在需要删除ip地址是192.168.1.1的段,可以这样: 
  13. sed -i '/define host/{:a;N;/}/!ba;/192\.168\.1\.1/d}' file 
  14. #注意和前一个例子中的区别  

t label在一次输入后有成功执行的s替换命令才跳转到label,如果省略了label,则跳转到整条命令结尾(即开始下一次读入)

  1. #如行列转换 
  2. [root@centos7 ~]# seq 10|sed ':a;$!N;s/\n/,/;ta' 
  3. 1,2,3,4,5,6,7,8,9,10 
  4. [root@centos7 ~]# 
  5. #如将MAC地址78A35114F798改成带冒号的格式78:A3:51:14:F7:98 
  6. [root@centos7 temp]# echo '78A35114F798'|sed -r ':a;s/\B\w{2}\b/:&/;ta' 
  7. 78:A3:51:14:F7:98 
  8. [root@centos7 temp]# 
  9. #这里\b表示匹配单词边界,\B表示匹配非单词边界的其他任意字符 
  10. #当然也可以采用其他的方式实现: 
  11. [root@centos7 temp]# echo '78A35114F798'|sed -r 's/..\B/&:/g' 
  12. 78:A3:51:14:F7:98 
  13. [root@centos7 temp]#  

T label在一次输入后只要没有替换命令被成功执行就跳转到label,如果省略了label,则跳转到整条命令结尾(即开始下一次读入)

z表示清除模式空间中内容,和s/.*//起相同的作用,但更有效。

更多例子

1、删除匹配行的上一行和下一行

  1. #例如输入数据为命令seq 10的输出(当然也可以是任意其他文件内容) 
  2. #要求删除匹配5那一行的前一行和后一行 
  3. [root@centos7 temp]# seq 10|sed -n '$!N;/\n5/{s/.*\n//p;N;d};P;D' 
  4. 10  

2、合并奇偶数行

  1. #输入数据为命令seq 11的输出,要求分别将奇数和偶数分别放在同一行 
  2. #输出***行`1 3 5 7 9 11`,第二行`2 4 6 8 10` 
  3. [root@centos7 ~]# seq 11|sed -nr '$!N;2!G;s/([^\n]+)\n((.+)\n)?(.+)\n(.+)/\4 \1\n\5 \3/;h;$p' 
  4. 1 3 5 7 9 11 
  5. 2 4 6 8 10  
  6. [root@centos7 ~]#   

3、合并多文件

  1. #文本a.txt的内容: 
  2. 01 12510101 4001 
  3. 02 12310001 4002 
  4. 03 12550101 4003 
  5. 04 12610001 4004 
  6. 05 12810001 4005 
  7. 06 12310001 4006 
  8. 07 12710001 4007 
  9. 08 12310001 4008 
  10. 09 12810101 4009 
  11. 10 12510101 4010 
  12. 11 12310001 4011 
  13. 12 12610001 4012 
  14. 13 12310001 4013 
  15. #文本b.txt的内容: 
  16. A 12410101 2006/02/15 2009/01/31 4002 
  17. B 12310001 2006/08/31 2008/08/29 4001 
  18. C 12610001 2008/05/23 2008/05/22 4002 
  19. D 12810001 1992/12/10 1993/06/30 4001 
  20. E 12660001 1992/05/11 1993/06/01 4005 
  21. #要求输出a.txt内容中第二列和b.txt中第二列相同的行,并追加b.txt中对应的两个日期列。 
  22. #形如:02 12310001 4002 2006/08/31 2008/08/29 
  23. sed -rn '/^[01]/ba;H;:a;G;s/^((..)( .*)( [^\n]+)).*\3(( [^ ]*){2}).*/\1\5/p' b.txt a.txt 
  24. #当然如果使用awk来处理的话,解决思路更容易理解一些: 
  25. awk 'NR==FNR{a[$2]=$3FS$4;next}{if($2 in a)print $0,a[$2]}' b.txt a.txt  

为加深对sed各种命令特性的理解,请自行分析这三个例子。

各种命令的组合使用,再加上正则表达式的强大能力,使得sed可以处理所有能够计算的问题。但由于代码可读性不强,理解起来比较困难,通常使用sed作为一个文本编辑器,对文本做非交互的流式处理。理解上述各个命令的含义,熟练使用它们,就会发现sed的强大之处。

责任编辑:庞桂玉 来源: segmentfault
相关推荐

2016-11-15 15:50:22

linux基础命令vim

2016-12-07 18:22:23

shelllinuxgrep

2016-12-23 10:56:34

linuxshellawk

2022-08-17 12:35:26

Linux sed编辑器

2021-01-07 11:00:59

Sed文本编辑器Linux

2016-12-08 22:26:28

2019-08-28 15:43:03

sed命令Linux

2024-10-28 16:42:04

Linux编辑器sed 命令

2016-12-29 11:13:30

shellbashlinux

2016-12-27 19:10:38

Linux命令启动流程

2023-07-03 22:35:50

Linux命令

2021-06-13 09:01:47

SedLinux命令

2009-07-31 17:38:47

linux vi命令详linux vi命令屏幕文本编辑器

2016-12-23 12:46:41

Linux命令进程与内存

2016-12-27 19:29:14

Linux命令定时任务

2016-08-22 11:01:47

Linux命令行文本编辑器

2019-09-12 08:32:40

Linuxsed命令语法

2021-09-21 15:28:33

LinuxSed智能引号

2016-12-14 19:24:41

2016-12-27 10:34:57

Linux命令软件包管理
点赞
收藏

51CTO技术栈公众号