[TOC]

sed全名stream editor,流编辑器。与awk一样,它们都是行编辑器,按行进行逐行文本操作。awk侧重点是分割和重新合成,而sed的侧重点是替换

参数

参数 作用
-e 指定sed命令
-f 读取指定的文件内容作为sed命令
-n 不输出原文件内容
-i 直接修改原文件,而不是默认的将修改后的内容输出到标准输出

输入

  • 读取文件内容作为输入

sed [command] file

  • 从标准输入中读取数据

echo -e "raw data" | sed "H;g"

输出

  • 输出到标准输出,不会修改文件

sed [command] file

  • (直接)修改文件

sed -i [command] file

  • (重定向)修改文件

sed [command] file > file

替换(s命令)

  • 匹配pattern替换为str

sed "s/pattern/str/g" file

  • 指定替换的行号范围

sed "START,ENDs/pattern/str/g" file

s命令最后的g表示对整行所有的匹配项都执行操作;将g替换为数字则表示仅对第n个匹配项执行操作;替换为数字+g则对第n个匹配项(包含)和它之后的匹配项执行操作。没有g、n时默认为匹配第一个,即等同于n=1。

  • 只替换第n个匹配的pattern

sed "s/pattern/str/n" file

  • 替换第n个及以后的匹配

sed "s/pattern/str/ng" file

  • 替换第5行以后的匹配

sed "5,$s/pattern/str/g" file

  • 多匹配(同时各自独立生效的匹配替换,不要求多个s命令都匹配才执行替换)

sed '1,3s/my/your/g; 3,$s/This/That/g' file
sed -e '1,3s/my/your/g' -e '3,$s/This/That/g' file

  • &符号:放在str段内表示被pattern成功匹配的字符串
    将hello替换为hello,world:
    sed "s/hello/&,world/g"
    0-9的数字会被替换为[0]、[1]、[2]……:
    sed "s/[0-9]/[&]/g"

  • ()圆括号:匹配串分组

    ()(使用的时候必须转移:\( \)表示分组的匹配,而()表示字面意义的括号)内的正则表达式pattern匹配到的字符串成为一个分组group;使用\1、\2等来引用这些分组

将hello, world替换为worldhello:
sed "s/\(*\), \(*\)/\2\1/g"

连行命令(N命令、n命令)

N命令会额外读入下一行参与进行匹配,也就是说,N命令会让原本逐行处理的匹配模式转变为逐两行处理

  • 每隔两行合并为一行(将下一行拼接到本行尾部)

sed "N;s/\n//" file

通常N命令结合s命令时,s命令往往仅做有限次的匹配,否则没有必要连行,或者会出现不理想的替换效果

n命令读取下一行并替换掉当前行(不会与当前行合并)

文本插入追加命令append与insert(a命令与i命令)

  • 在第1行前插入

sed "1 i Insert in the front of line 1." file

  • 追加文本(在最后一行之后追加)

sed "$ a The end." file

  • 结合模式匹配(在有world单词所在的行之后插入感叹号)(注意是所在行之后,不是匹配项之后)

sed "world/a !" file

文本替换命令(c命令)(与s命令不同在于,s命令替换匹配项而c命令替换整行)

使用方法与a命令、i命令完全相同,只是替换为c表示c命令即可

文本删除命令(d命令)

使用方法同a、i命令

  • 仅保留前10行同时插入# 到行开头。

sed "10,$d; s/^/# /g" file

打印匹配项(p命令,sed的grep模式)

  • 仅打印出匹配的项,几乎等同于执行文本搜索指令如grep、rg

sed -n "/hello/p" file

  • 打印前10行

sed -n "1,10/p" file

  • 从第10行开始打印,直到匹配hahaha的行

sed -n "10,/hahaha/p file"

  • 从匹配spring的行开始打印,直到匹配winter的行

sed -n "/spring,/winter/p file"

多命令与命令嵌套

  • 多个命令使用分号分隔
    把war所在的行、emperor单词替换为Revolution:

sed "/war/d;s/emperor/Revolution/g" file
多条件有时候需要大括号包装以被同一个匹配选项所修饰,从第1行到第10行执行:
sed "1,10{/war/d;s/emperor/Revolution/g}" file

多命令相当于并列条件,各匹配条件各自为政,任何一个条件只要被满足就会执行,相当于逻辑或

  • 命令嵌套
    使用大括号打包一个命令:

sed -n "{/world/p}" file
使用嵌套的打括号嵌套多个命令,匹配outer成功后进一步匹配inner:
sed -n "{/outer/{/inner/p}}" file

命令嵌套相当于要求多条件都同时满足进行匹配

保持空间(Holding Space)

sed将输入的信息读取到一个缓冲区中进行处理,这个缓冲区被称为模式空间(Pattern Space)。常规的命令均是针对模式空间的,如读入的数据存储在模式空间,文本的替换、打印到输出也是在模式空间中完成的。如p命令打印实际上就是将模式空间的内容输出。

而sed还有一个称为保持空间(Holding Space)的区域。这块区域提供给用户和一些命令,允许它们临时存储一些内容来实现一些需求和功能。

Space 说明 作用
模式空间Pattern Space 数据的缓冲区,sed每次处理一行数据时都将数据存储于Patern Space,一些命令也基于该缓冲区工作,如s命令在这里进行匹配和替换,p命令打印的内容就是该区域的内容 sed处理文本的基础缓冲区
保持空间Holding Space 通过一些命令能够将模式空间的内容拷贝或追加到该区域以实现缓存。当sed处理下一行时,模式空间会存放下一行的内容,而保持空间的内容则不变。通过反向的命令可以实现将保持空间的内容拷贝或追加到模式空间 当需要暂存模式空间的内容或让数据在两个空间之间流转时使用

由于保持空间是用于辅助实现功能的,且sed执行替换、打印等命令均基于模式空间(也就是说sed运行结束时或每行处理完成后只有模式空间的内容才是提供结果的)。所以通常情况下,从模式空间流转进入保持空间的数据往往都是“暂时”的——一般会通过反方向的命令把他们再传递到模式空间。

命令 功能 说明
h 用模式空间中的内容替换掉保持空间 暂存模式空间的内容,保持空间原本的内容被替换掉
H 将模式空间中的内容追加到保持空间 同上,但是是追加
g 用保持空间中的内容替换掉模式空间 h的反方向操作
G 将保持空间中的内容追加到模式空间 G的反方向操作
x 交换模式空间和保持空间中的内容
  • 例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    cat 123.txt
    one
    two
    three

    sed "H;g" 123.txt
    one
    one
    two
    one
    two
    three

    对每一行均执行了H然后执行g。在第一行,模式空间为one,保持空间为空,执行H操作使得模式空间的one追加到保持空间,保持空间此时为one;紧接着g指令将保持空间替换掉了模式空间,由于此刻两者内容都是one因此替换后仍然也都是one。sed没有-n参数因此在本行处理完成后打印出模式空间的内容one。
    然后,处理第二行,相同的逻辑,模式空间的two被追加到保持空间。保持空间原来已经有了one,此时追加后变成one\ntwo。同样的经过g命令替换到模式空间,模式空间的two被替换为one\ntwo,因此出现这种打印。

  • 例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    cat data.txt
    one
    two
    three
    last
    sed -n '/two/ {h ; p ; n ; p ; g ; p }' data.txt
    two
    three
    two

    匹配two后存储到保持空间,然后打印two。通过n指令读取下一行并p打印three。接着通过g指令将保持空间的内容替换掉模式空间,此时模式空间原本的three被替换为two后由p指令打印。

速查表

实际上就是正则表达式(Vim)

命令 作用 案例
^ 匹配文本开头 sed -i "s/^/#/g" file 每行开头加上#
$ 匹配文本末尾 “s/. $/./g” 去除末尾句号后的空格
\<prefix 匹配以prefix开头的字母
suffix\> 匹配以suffix结尾的字母 vim里面全字匹配搜索单词word:\<word\>
. 匹配任意单个字符
* 某单个字符出现多次或0
+ 某单个字符出现多次且至少1次
[a-zA-Z0-9] 集合匹配
[^a] 除a以外
s 替换 s/p/s/g全文替换;s/p/s/1仅每行的第一个匹配项被替换;s/p/s/2g每行的第2个(包含)以后的匹配项被替换;5,$s/p/s/g从第5行开始
& 代表被pattern匹配的字符串 在s命令中,s/pattern/str/g,str中可以使用&表示pattern,如s/pattern/&s/g表示将pattern替换为patterns
= 匹配后输出行号 在命令中使用=后,匹配结果输出时先输出该结果对应的行号,然后再换行输出匹配的字符串(当-n参数时不输出)[address1[,address2]]=;[/pattern/]=;如”/war/ =”、1,$=
\(\) \1 \2 分组匹配 通过\1 \2来引用被\( \)分组的匹配项
N 连行命令 额外读入下一行一起匹配
a 行追加 除s命令外的其他模式匹配都是将命令写在模式指令字符后的,如”/hello/a”表示在有hello行后追加文本;s命令则写在开头如”s/hello//“
i 行插入
c 行替换 s命令替换匹配项,c命令替换整行
p 打印匹配项 需要结合-n参数,否则由于sed默认打印出被处理的原文件;除了可以用正则表达式进行匹配外,还可以指定行号。也可以混用:”3,/war/p”从第3行开始打印直到匹配了war的行
{} 嵌套命令 删除有war和emperor的行:”{/war/{/emperor/d}}”;有时候也用作包装:”1,10{N;s/war//g;/poor/d}”,包装这三个并列的命令,让他们均接受1,10的限定

经典示例

  1. 删除空行/^$/d
  2. 过滤空行(不打印空行)/^$/!p
  3. 打印文件行数(仅匹配最后一行,匹配成功打出行号)sed -n "$ ="

参考链接

  1. sed参考手册
  2. CoolShell sed简明教程
  3. sed教程
  4. 正则表达式入门