山地人

Linux sed命令

山地人
山地人
2021-05-24

这是一篇关于sed的教程,通过这篇教程的学习,你会学到这些sed技能:

  1. sed是什么
  2. sed的结构
  3. sed如何使用

什么是sed

sed是一种流编辑器,你可以把它想象成一个加工厂,将流入的文本进行加工处理然后输出。

sed的结构

下面是sed命令的基本格式。

sed [options] [SCRIPT] [INPUTFILE...]
  • [options] 是可以为sed配置的一些选项参数
  • [SCRIPT] 这里是用来对文本进行处理的主要部分,都是一些命令
  • [INPUTFILE...] 是指定的要处理的文件的路径

你现在只需要对这个结构有个大致印象即可,随着我们一点点展开,你会对这些结构越来越清晰。

文字替换

替换操作会用到script中的s命令s命令的语法格式如下:

s/regexp/replacement/[flags]
  • regexp是要查找匹配的正则
  • replacement是替换内容
  • [flags]是可选的,用来添加一些额外标记参数

假设我们有一个文件hello.txt,这个文件里的内容如下:

Hello World!

我们想要把后面的World替换成Sed。我们可以这样来写:

sed 's/World/Sed/' hello.txt

简单分析下:

  • 's/World/Sed/'就是[SCRIPT]脚本命名,这里用的是s命令
  • s命令后面用三根/斜杠分了两个区域,World是要查找的字符,Sed是替换后的字符。
  • hello.txt是要sed要处理的文件路径。
  • 最后处理完的结果会显示到标准输出,也就是屏幕上。

启动终端,测试下这个脚本。(hello.txt已经为你创建好了)

保存结果

当然,你应该不会满足于让处理的结果只显示在屏幕上,要想把处理后的结果保存到文件中,这里有几种做法。

重定向输出

下面的>就是重定向输出,后面的result.txt就是重定向的去处,通过这个操作,原本输出到屏幕的文字就是乖乖写入到result.txt中了。

sed 's/World/Sed/' hello.txt > result.txt

sed写入

当然sed本身也有写入文件的操作,通过w命令后面跟随要写出结果的文件路径即可。当然别忘了w后面要至少留一个空格。

sed 's/World/Sed/w result2.txt' hello.txt

使用这种方式,sed会往屏幕和result2.txt两个地方同时输出内容

启动终端,测试下两种写入操作。(可以用cat result.txt查看下写出是否成功)

修改原文件

还有一种方式可以让sed将本次处理的结果直接写入到原文件中。利用[options]部分的-i选项,当然这个操作一旦执行,原文件的内容就会被修改掉,所以此操作需谨慎。

sed -i 's/World/Sed/' hello.txt

你可以在虚拟环境中做下这个操作。然后用cat hello.txt查看hello.txt是否被修改掉了。

全局替换

现在我们拿到一个文件story.txt,这个文件里的内容如下:

this is a book!
the book is that girl's.
the girl is over there.
the girl wants to go to the park.

如果我们要把这个文件中的t换成T,利用刚才学过的知识,我们有了下面的方案:

sed 's/t/T/' story.txt

动手试一试,看看结果如何

完成的不错,所有开头的t都换成了T,竟然知道只替换开头的第一个t,太”智能”了。但仔细想想,我们想要的是把所有出现的t都替换掉。默认情况下sed在替换的时候,对每一行的内容只替换一次。

我们需要改进脚本,在末尾加上一个标记g,表示全局替换。

sed 's/t/T/g' story.txt

再试一下,看看这次是否都替换掉了。

虽然结果看起来不那么舒服,但是这确实完成了我们的目标。

选择处理范围

使用sed命令,除了对全文进行操作,你也可以选择一个范围进行处理。

  • 指定要处理的具体行,如5第5行
  • 给定一个范围,如:1,3第1-3行,10,$第10行到结尾。
sed '2,3 s/t/T/g' story.txt

这里2,3就是指定了要处理的范围,你可以换成其他值试试

追加、删除和修改

前面我们讲[SCRIPTS]部分讲到了s命令用来做替换的,除了替换命名,sed还支持一些其他的操作:

  • a\新追加内容 也可以写成a 新追加内容:这个命令是append用来在匹配位置后方追加内容。
  • i\新插入内容 也可以写成i 新插入内容:这个命令是insert用来往匹配位置前面插入内容。
  • c\新修改内容 也可以写成c 新修改内容:这个命令是change用来修改匹配位置的内容。
  • d这个命令是delete用来删除匹配内容

下面的例子让2-3行后方分别append追加一行----

sed '2,3 a ----' story.txt
# 等价于
sed '2,3 a\----' story.txt

将a命令换成ic,看看结果有何不同。

删除第2-3行的内容

sed '2,3 d' story.txt

试试删除命令d

sed里的两个空间

在sed对文本进行处理的时候,会用到两个空间(缓冲区):pattern space模式空间和hold space保持空间。

pattern space

对于绝大多数的命名操作,其实都是在和pattern space打交道,上面在分析debug的时候看到的PATTERN就是指的pattern space模式空间。它是用来放置正在处理的文本和操作结果的一个缓冲区。p命令打印输出的也是这个空间的内容。

hold space

hold space保持空间就像是一个包裹寄存箱。帮pattern space管理一些需要暂时存放的数据,等pattern space模式空间需要的时候,可以把这里存放的数据再取出来使用。 有些命令的操作会涉及到hold space,比如:

  • h命令将当前pattern space中的内容替换掉hold space中的老内容。
  • g命令的操作和h正好相反,从hold space中取出内容放入pattern space

选项参数

下面讲讲常用的OPTIONS选项参数

-n--quiet--silent:这三个是等价的选项,启用后只有在命令中主动用p命令输出的匹配行才会输出。

sed -n '2p' story.txt

测试下,加上和不加-n看看结果有何变化。

--debug可用于调试查看sed的每次循环的执行过程,这对于分析sed非常有帮助。

sed --debug -e 's/^t/T/; 2d; 4d' story.txt

开启debug选项后,运行获得如下结果:

SED PROGRAM:
s/^t/T/
2 d
4 d
INPUT: 'story.txt' line 1
PATTERN: this is a book!
COMMAND: s/^t/T/
MATCHED REGEX REGISTERS
regex[0] = 0-1 't'
PATTERN: This is a book!
COMMAND: 2 d
COMMAND: 4 d
END-OF-CYCLE:
...
  • SED PROGRAM中记录了这条sed中共有3条脚本命令
  • INPUT部分是要处理的输入
  • PATTERN是当前放入PATTERN缓冲区的内容
  • COMMAND: s/^t/T/是当前执行的第一条命令
  • MATCHED REGEX REGISTERS因为这次执行中正则部分是^t,所以匹配了文本中的t
  • 完成第一条命令的处理后:PATTERN缓冲区文字变成了This is a book!
  • 后面的两条COMMAND: 2 dCOMMAND: 4 d因为限定范围为24行所以没有命中
  • END-OF-CYCLE表明第一次循环到此结束。
  • 后续的debug过程以此去分析就可以了。

对于后续遇到复杂的sed或者自己没有理解的sed命令可以用这种方式去分析运行流程,还是非常有用的。

更多命令

有了前面的模式空间保持空间的概念,已经掌握了debug技术后,再来理解这些命令就会变得相对容易。毕竟实在不懂,还可以用debug调试分析过程。

下面这张表列举了一些常用的sed命令:

命令说明
p打印模式空间
q[exit-code]Q[exit-code]退出sed程序,exit-code可以设置退出状态码
w filename将模式空间内容写入filename路径指定的文件
W filename将模式空间部分内容写入filename路径指定的文件,只写出遇到第一个换行符就停止,所以只输出1行内容。
x交换模式空间和保持空间里的内容。
g使用保持空间中的内容替换模式空间的内容(模式空间原来的内容会被清除)
G在模式空间中追加以个新行,之后把保持空间的内容添加到模式空间(和g唯一的区别是会保留原来的内容)
h使用模式空间中的内容替换保持空间中的内容(和g命令相反)
H在保持空间中追加以个新行,之后把模式空间的内容添加到保持空间(和G操作相反)

p命令

p命令的全称是print,是用来打印当前模式空间的内容,我们可以做下面这个实验:

sed --debug -e 'p' story.txt

开启debug后,看看每次p命令执行都有哪些影响。

启动终端,进行实验。

运行完上面的实验,得到这样的结果,内容比较长,我截取了前两次循环的过程。

SED PROGRAM:
p
INPUT: 'story.txt' line 1
PATTERN: this is a book!
COMMAND: p
this is a book!
END-OF-CYCLE:
this is a book!
INPUT: 'story.txt' line 2
PATTERN: the book is that girl's.
COMMAND: p
the book is that girl's.
END-OF-CYCLE:
...

一起分析一下:

  • 整个sed程序的命令部分就一个p,没有设置任何范围限定,所以p会对每一行文本都执行
  • INPUT 输入流目前处于story.txt中第一行的位置
  • 此时PATTERN模式空间中存放的内容是story.txt文本中的第一行内容this is a book!
  • 执行第一次COMMAND: p后,输出了this is a book!。输出的内容就是来之与上面的PATTERN模式空间。

你可以顺在这个过程继续看后续的执行流程了,这里不再赘述。

q命令

利用q命令,在执行两条语句后,退出sed。

sed -e '2q' story.txt

另外q命令可以携带一个退出码exit-code,用来表明本次是否为异常退出。 退出后,退出码exit-code的值可以用$?查询到。

sed -e 'q2' story.txt
echo $?

g、G、h和H

这四个命令是两组在模式空间和保持空间内容迁移的命令。 g和h是一组,g是替换模式空间内容,h是(hold)用来替换保持空间的内容。 G和H是一组,比上面那一组,多了保留原来空间内容的功能。

为了更清楚得认识他们,我们做一组实验:

seq 3 | sed --debug -e 'H'

上面使用seq 3生成一个如下的1-3的序列:

1
2
3

然后通过|管道命令发送给后面的sed处理。

启动终端,测试并分析debug执行流程。

执行完上面的sed命令,我们会得到下面的结果:

这里我只截取了两个循环的打印结果,已经足够说明问题

SED PROGRAM:
H
INPUT: 'STDIN' line 1
PATTERN: 1
COMMAND: H
HOLD: \n1
END-OF-CYCLE:
1
INPUT: 'STDIN' line 2
PATTERN: 2
COMMAND: H
HOLD: \n1\n2
END-OF-CYCLE:
2

我们先回忆下H命令的定义:

H: 在保持空间中追加以个新行,之后把模式空间的内容添加到保持空间

分析过程

seq 3发送过来的数据是下面这个样子:

1
2
3

第一次循环中

  • INPUT定位在标准输入的第1行,此时模式空间读入了1
  • 运行COMMAND: H后,HOLD: \n1,在保持空间中添加一个新行\n然后追加了模式空间的1到保持空间中,所以最终得到\n1,第一次循环结束。同时这个模式空间的1也被输出到了屏幕。 第一此循环
  • INPUT定位到标准输入的第2行,此时模式空间读入了2
  • 运行COMMAND: H后,在保持空间中添加一个新行\n,此时保持空间值为\n1\n然后把模式空间的2追加到了保持空间现有内容后,最后得到\n1\n2。同时模式空间的2也被输出到了屏幕上。 这就是H的执行过程,你可以沿着这个思路再去分析gGh命令,应该都会比较顺利了。

执行多条命令

如果你要在同一个文件上执行多次操作,有两种方式:

  • { cmd; cmd } 你可以执行多条命名中间用;分号分开
  • -e script -e script 也可以用多条-e语句,这里的-e的全量写法是--expression

比如对于story.txt文件,我们希望将所有开头的t换成T,并删除第2和第4句。

sed -e 's/^t/T/' -e '2d' -e '4d' story.txt
# 等价于
sed -e 's/^t/T/; 2d; 4d' story.txt

启动终端,试试不同的组合写法

如何自学

至此,你已经掌握了sed的大部分操作,但一篇教程的内容毕竟篇幅有限,下面讲讲遇到一些新命令或者新问题改如何应对:

  1. 查帮助手册man sed,不管是sed命令,还是其他命令,man手册绝对是你学习Linux命令的必备。
  2. 如果看手册+调试分析还是不能搞定,那就充分利用网络google或到Stack Overflow上看看是否有人解决过类似问题,绝大多数时候,你遇到的问题,人家也都会遇到,并且绝大多数都已经解决掉了,耐心去顺找,并阅读理解掉。

至此,本篇教程也就到了该和你说再见的时候了,我们下期再见。

学完本篇互动教程,如果你觉得体验不错,可以把网页链接发送给你的小伙伴,让他/她也来感受一下。当然,你也可以继续看看网站上其他的的互动教程,希望`idev365`能够给你带来收获。

学习教程的过程中碰到了问题,或者对idev365有什么改进意见和想法,欢迎加入idev365微信内测群,和山地人交流你的想法。