天天看点

sed命令小叙什么是sedsed的工作原理

什么是sed

sed是个非交互式的流编辑器(non-interactive stream editor)。所谓非交互式的意思就是说,sed必须使用指定的脚本(script)来处理文本流,脚本可以通过命令行(-e)或者从文件(-f)中获取,换一种说法就是sed是以批处理的方式来处理文本流的。而所谓流编辑器的意思就是说,sed每次从文本流(文本流可以来自文件,也可以直接来自标准输入)中读出一行,然后对该行进行处理,最后输出处理过的文本内容(可以输出到标准输出,也可以是文件中)。接着继续读取下一行,做相应的处理,然后输出,如此循环,直到整个文件流处理完毕。

在SHELL脚本中,sed的最主要用途是用于文本替换(text substitution)。

sed的工作原理

如果想充分地了解sed的工作原理,则必须弄清楚sed的工作流程以及两个重要的概念:模式空间(pattern space)和暂存空间(hold space)。本小节力争阐释清楚这些概念。

在GNU sed的官方文档中6.1 How `sed' Works是这样阐释的:

`sed' maintains two data buffers: the active _pattern_ space, and the auxiliary _hold_ space. Both are initially empty.

`sed' operates by performing the following cycle on each line of input: first, `sed' reads one line from the input stream, removes any trailing newline, and places it in the pattern space.  Then commands are executed; each command can have an address associated to it: addresses are a kind of condition code, and a command is only executed if the condition is verified before the command is to be executed.

When the end of the script is reached, unless the `-n' option is in use, the contents of pattern space are printed out to the output stream, adding back the trailing newline if it was removed.(1) Then the next cycle starts for the next input line.

Unless special commands (like `D') are used, the pattern space is deleted between two cycles. The hold space, on the other hand, keeps its data between cycles (see commands `h', `H', `x', `g', `G' to move data between both buffers).

逐句翻译如下:

sed程序在内部维护有两个数据缓冲区,分别为使用频繁的模式空间(pattern space)和另一个偶尔使用的辅助暂存空间(hold space)。在程序运行的一开始,这两个缓冲区都是初始化为空的。

sed按照如下循环操作来处输入流中的每一行:首先,sed从输入流中读取一行文本,先去除尾部的换行字符,之后再放入模式空间(pattern space),最后才会使用指定的命令脚本(commands)来处理模式空间(pattern space)中的文本内容。每一个命令脚本(commands)都可以有一个“地址范围”,这个地址范围就像一个前提条件一样,只有符合该条件之后才会运行命令脚本去处理文本。

当所有的命令脚本都运行完之后,除非指定了`-n'选项,否则都会将模式空间中处理过的文本在尾部加上之前去除的换行字符,再将其输出到标准输出。至此,输入流中的一行就完整地处理完了,接着会继续读取下一行做同样的流程处理。

除非指定了一些特殊的命令(比如命令`D'),否则在两个循环之间都会将模式空间(pattern space)中的文本删除清空。而在两个循环之间,暂存空间(hold space)的内容却是保持不变的。

sed的工作流程图如下:

sed命令小叙什么是sedsed的工作原理

sed基本工作流程

sed命令小叙什么是sedsed的工作原理

明白了这个工作流程之后就很容易理解sed提供的一些选项和命令的意义了,它们中有相当一部分是用来控制影响或者改变sed工作流程的,例如-n选项就是用来禁止将模式空间(pattern space)中修改过的内容打印到标准输出;-i选项也不需要将模式空间中的内容打印到标准输出,而是直接修改输入流。此外,sed的命令d是删除模式空间中的内容后忽略后续命令,并且跳过“加上换行符,打印到标准输出”这些步骤,直接开始判断是否已经到达文件结尾。

我们可以把sed的命令格式简化如下:

sed [-Options] ‘[address-range][sed-command]’ filename

其中的-Options最常用的有如下几个:

  • -e和-f:-e和-f选项用于指定sed的命令脚本从哪里获得,-e说明是从命令行直接获得,而-f说明从指定的文件获得。
  • -n:使用安静模式,即默认不将模式空间中的内容打印到标准输出,这个选项一般与命令p配合使用。
  • -i:当需要直接修改文件时,-i选项就很有用。
  • -s:当sed一次处理多个文件时,默认将他们看成一个整体,即行号是从1开始直到结束,但是使用了-s后,sed就会分别对待各个文件为单独的流。

对于其中的address-range有很多种方式来指定,大体可以分为两类:一类是直接给出行号,另一类是通过正则表达式来匹配符合要求的行。当然,这两者可以混合使用。现将最常用的几种方式归纳如下:

  • 没有给出任何address-range的信息,则后续的命令默认作用于输入流的每一行。
  • `NUMBER' -- 直接给出一个行号,那么后续的命令只会在该行上执行。
  • `FIRST~STEP' -- 匹配从第FIRST行开始的每STEP行记录,例如:1~2匹配1,3,5,7,...;2~2匹配2,4,6,8,...;2~3匹配2,5,8,....
  • `$' -- $符号在这里代表的是文件中的最后一行,这与在正则表达式中代表一行的末尾是有区别的,这点要格外注意。此外,sed没有给出指定第一行的特殊符号,如果需要就直接给出行号1就OK。
  • `/REGEXP/' -- 通过给定的正则表达式来匹配符合要求的行号。此外,后续的//代表的是最近一次成功的匹配,使用这种方式可以使得命令更为简洁。
  • `\%REGEXP%' -- 这种方式和`/REGEXP/'一样,只不过我们可以根据实际需要,指定其它定界符(delimiter),而不是/。
  • `/REGEXP/I'或者`\%REGEXP%I' -- 这种形式可以让sed忽略大小写。
  • `0,/REGEXP/' -- 注意它与`1,/REGEXP/'的区别:`0,/REGEXP/'默认会从输入流的第一行开始匹配REGEXP,而`1,/REGEXP/'则不会,直到REGEXP的二次出现才会停止。此外,这里的REGEXP必须是正则表达式。
  • `ADDR1,+N' -- 匹配的是从行号ADDR1开始加上之后的N行,注意ADDR1可以是行号,也可以是正则表达式。
  • `ADDR1,~N' -- 匹配的是从行号ADDR1开始直到之后的行号是N的整数倍为止。 同样的,ADDR1可以是行号,也可以是正则表达式。

最后,上面的各种address-range形式后头都可以追加一个感叹号“!”,代表的意思是反向选择的意思。

sed提供了很多有用的命令,我把最常用的归纳如下:

  • `d' -- 删除模式空间中的文本内容,忽略后续的命令,并且立即开始下一个循环。
  • `p' -- 打印模式空间中的文本内容到标准输出。这个命令一般都会和选项-n配合着使用,这样就可以只打印出符合要求的文本内容。
  • `w FILENAME' -- 将模式空间的内容写入文件FILENAME。
  • `a\' -- 新增, a 的后面可以接字串,而这些字串会在新的一行出现(目前的下一行)。
  • `i\' -- 插入, i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行)。
  • `c\' -- 取代, c 的后面可以接字串,这些字串可以取代 n1,n2 之间的行。
  • `{ COMMANDS }' -- {}中可以有一组命令,这样就可以在一个address-range上执行不止一条命令了。
  • `n' -- 先打印出模式空间的文本内容(如果没有指定-n选项的话),紧接着读取下一行内容替换掉模式空间内容,并且使用后续命令脚本处理,最后按照sed的正常流程工作。示意图如下:
sed命令小叙什么是sedsed的工作原理

使用n命令后的sed工作流程

sed命令小叙什么是sedsed的工作原理
  • 命令N -- 与命令n的区别是它并不删除模式空间的内容,而是增加一个新的换行之后,将下一行内容追加在其后。
  • 命令s -- 这是sed程序使用最频繁的一个命令,使用它可以实现各种替换。下面着重介绍它。

命令s的格式如下:

s/REGEXP/REPLACEMENT/FLAGS

其中的定界符(delimiter)可以换成其它任意字符,而当REGEXP和REPLACEMENT中出现定界符时,需要反斜杠来转义。

命令s提供了非常多的选项操作,但是它的基本思路始终是不变的:试着在给定的模式空间的文本内容中匹配REGEXP,匹配到之后用REPLACEMENT替换掉。

关于REPLACEMENT,有两点比较重要,需要了解:

  • REPLACEMENT中可以包含\N(N是1~9的数字),代表的意思是“从这里开始替换成匹配第N个圆括号里子表达式的文本”。
  • REPLACEMENT中未经转义的特殊符号&代表的意思是“从这里开始替换成匹配于正则表达式的整个文本”。

此外,命令s还提供了几个非常有用的标志(flags),如果你不使用任何标志,那么默认只替换第一个匹配的。常用的标志如下:

  • `g' -- 全局性(global),替换所有的匹配项,而不仅仅是第一个匹配项。
  • `NUMBER' -- 我们也可以指定一个确切的数字来告诉sed我们想替换第几个匹配项;如果我想从第N个匹配项替换到最后,该怎么做呢? 只要指定Ng就可。
  • `p' -- 如果替换成功,则打印被改变后的这行文本,常结合 -n 选项使用(想想使用场景:你只希望打印出发生过替换的文本内容,而不是所有的)。
  • `w FILE-NAME' -- 将发生过替换的文本写入文件FILE-NAME。
  • `I'或者`i' -- 英文字母‘i’的大写或者小写告诉sed的s命令,在匹配的过程中,忽略字符的大小写。
  • `e' -- 将替换后的模式空间中的文本当做命令来执行,并将执行结果写回模式空间。

这些标志可以自行根据需要组合使用。

需要补充说明的是,sed-command可以不止一条命令,可以是一组命令。当是一组命令时,需要在每个命令前加上选项-e,表示是需要执行的命令,如下:

sed [-Option] -e [sed-command-1] -e [sed-command-2] ... filename

这种情况下,我们可以通过反斜杠分成多行,实例如下:

$ sed -n \

-e '/^root/ p' \

-e '/^nobody/ p' \

/etc/passwd 

此外,你也可以把通过花括号把要执行的多个sed命令写到一组,其基本语法为:

sed [-Options] '{

sed-command-1

sed-command-2

}'  filename

实例如下:

$ sed -n '{

/^root/ p

/^nobody/ p

}' /etc/passwd

实例演练

接下来我们通过具体的实例来熟悉一下sed的使用吧。

参考链接:

《Complete Sed Command Guide [Explained with Practical Examples]》(中文版)

《sed从入门到深入的使用心得》

《Sed基本入门》

《Sed and awk 笔记之 sed 篇:简单介绍》

《Linux命令的工作原理(1)—— sed的工作原理》

《sed全面实用版!》

《sed命令简单使用-pattern space与hold space》

《Sed 命令地址匹配问题总结》

《【知识涌升】sed再进阶》

《sed 高级用法(1) 之 n N》

《[9-15]Sed文本处理——高级用法》

《Using shell variables in sed》

上一篇: Spark Core
下一篇: shell中的sed

继续阅读