学习Sed文本编辑器
Sed 代表 Strem Editor(流编辑器),是操作、过滤和转换文本内容的强大工具。 它最初是为 AT&T 最初的 Unix 操作系统第 7 版创建的,此后可能每一个 Unix 和 Linux 操作系统都包含了它。 sed 以行为处理单位,针对每一行进行处理。 功能上同awk类似,sed功能更简单,针对列处理功能要差很多。
1、sed 命令语法
Section titled “1、sed 命令语法”sed [options] '{sed-commands}' {input-file}可选参数
-n, --quiet, --silent: 仅显示sed-commands处理后的结果-e script, --expression=script: 指定sed-commands来处理输输入的文件-f script-file, --file=script-file: {sed-commands}即可是单个命令也可以是多个命令,将多个命令合并到一个文件(被称为:sed脚本)后,可以使用-f选项调用它-i: 直接修改原始文件内容(危险动作,需要慎重使用)--version: 显示版本号
2、sed 脚本执行流程
Section titled “2、sed 脚本执行流程”sed脚本执行的步骤很容易记住:读取(Read),执行(Execute),打印(Print),重复(Repeat)。我们可以利用这几个步骤的首字母REPR来记忆sed执行的步骤。
我们来看一下这几个步骤。 sed将会:
- 读取一行内容到模式空间(sed内部的一个临时缓存,用于存放读取到的内容)
- 执行:对模式空间里的内容执行sed命令。 如果使用了 {} 或 -e 指定多个命令,sed将依次执行每个命令
- 打印(输出)模式空间的内容。然后清空模式空间
- 重复以上流程,直到文件结束

3、sed 命令
Section titled “3、sed 命令”a: 追加命令,可以在指定位置后面插入新行i: 插入命令,在指定位置之前插入新行c: 修改命令,取代指定位置旧行d: 删除命令,删除指定行p: 打印命令,打印当前模式空间的内容,通常和可选参数-n一同使用s: 替换命令,进行字符串替换y: 转换字符,根据对应位置转换字符,如进行大小写转换=: 打印行号,会在每一行的后显示改行的行号q: 终止正在执行的命令并退出sed。sed正常执行流程是读取数据、执行命令、打印结果、重复循环。当sed遇到q命令时,便停止执行后续循环立即退出r: 从指定的文件读取内容,并在指定的位置将其打印出来
4、实例技巧
Section titled “4、实例技巧”有一段歌词保存在文件myheart,内容如下:
1 Every night in my dreams2 I see you, I feel you3 That is how I know you go on完整歌词可以在My Heaert Will Go On 获取完成歌词
4.1、追加行
Section titled “4.1、追加行”使用 a 命令可以在指定位置后插入新行,语法格式:
$ sed '[address] a the-line-to-append' input-file4.1.1、在第一行后追加一行
Section titled “4.1.1、在第一行后追加一行”$ sed '1a hello world' myheart1 Every night in my dreamshello world2 I see you, I feel youThat is how I know you go on在mac上使用系统自带sed版本执行时,会返回错误信息
sed: 1: "1a hello world": command a expects \ followed by text。 可以安装GNU版sed来解决:brew install gnu-sed
alias sed=gsed
4.1.2、在最后一行追加一行
Section titled “4.1.2、在最后一行追加一行”$ sed '$a Far across the distance' myheart1 Every night in my dreams2 I see you, I feel you3 That is how I know you go onFar across the distance4.1.3、在匹配行后追加一行
Section titled “4.1.3、在匹配行后追加一行”$ sed '/you/a Far across the distance' myheart1 Every night in my dreams2 I see you, I feel youFar across the distance3 That is how I know you go onFar across the distance4.2、新行取代旧行
Section titled “4.2、新行取代旧行”使用c命令可以,用给定的行替换指定位置的旧行。
4.2.1、用新数据取代第一行
Section titled “4.2.1、用新数据取代第一行”$ gsed '1c Far across the distance' myheartFar across the distance2 I see you, I feel you3 That is how I know you go on将第一行替换为Far across the distance
4.2.2、用新数据替代匹配you的行
Section titled “4.2.2、用新数据替代匹配you的行”$sed '/you/c You have come to show you go on' myheart1 Every night in my dreamsYou have come to show you go onYou have come to show you go on4.2.3、用多行新数据替代匹配you的行
Section titled “4.2.3、用多行新数据替代匹配you的行”$sed '/you/c Far across the distance\>And spaces between us' myheart1 Every night in my dreamsFar across the distanceAnd spaces between usFar across the distanceAnd spaces between us/you/匹配到了两行,所以这两行都进行了替换
4.2.4、每一行后新增空白行
Section titled “4.2.4、每一行后新增空白行”$ sed G myheart1 Every night in my dreams
2 I see you, I feel you
3 That is how I know you go on
G命令把当前保持空间的内容作为新行追加到模式空间中, 模式空间的内容不会被覆 盖,该命令在模式空间后面加上换行符\n,然后把保持空间内容追加进去。
4.5、删除行
Section titled “4.5、删除行”4.5.1、删除第一行
Section titled “4.5.1、删除第一行”$ sed '1d' myheart2 I see you, I feel you3 That is how I know you go on4.5.2、保留第一行其余都删除
Section titled “4.5.2、保留第一行其余都删除”$ sed '1!d' myheart1 Every night in my dreams4.5.2、删除最后一行
Section titled “4.5.2、删除最后一行”$ sed '$d' myheart1 Every night in my dreams2 I see you, I feel you4.5.3、删除匹配行
Section titled “4.5.3、删除匹配行”$ sed '/Every/d' myheart2 I see you, I feel you3 That is how I know you go on4.5.4 删除2开头的行
Section titled “4.5.4 删除2开头的行”$ sed '/^2/d' myheart1 Every night in my dreams3 That is how I know you go on4.5.5 删除从第一次匹配行到最后一行
Section titled “4.5.5 删除从第一次匹配行到最后一行”$ sed '/^2/,$ d' myheart1 Every night in my dreams4.5.6 删除从第一次匹配行和它后面的一行
Section titled “4.5.6 删除从第一次匹配行和它后面的一行”$ sed '/^2/,+1 d' myheart1 Every night in my dreams4.5.7 删除从第一次匹配行到第二行
Section titled “4.5.7 删除从第一次匹配行到第二行”$ sed '/Every/,2 d' myheart3 That is how I know you go on4.6、搜索显示数据
Section titled “4.6、搜索显示数据”4.6.1、显示第一行
Section titled “4.6.1、显示第一行”sed在执行完成命令后会默认打印模式空间的内容,而命令p也会输出当前模式空间的内容。所以,
通常使用命令p时,还需要使用-n选项来屏蔽sed的默认输出,否则使用p命令后,每行记录会输出两次。
$sed -n '1p' myheart1 Every night in my dreams4.6.2、显示最后一行
Section titled “4.6.2、显示最后一行”$ sed -n '$p' myheart3 That is how I know you go on4.6.3、显示第二到第三行
Section titled “4.6.3、显示第二到第三行”$ sed -n '2,3p' myheart2 I see you, I feel you3 That is how I know you go on4.6.4、搜索显示匹配的行
Section titled “4.6.4、搜索显示匹配的行”$ gsed -n '/Every/p' myheart1 Every night in my dreams显示包含Every的行
4.7、 替换数据
Section titled “4.7、 替换数据”sed除了整行的新增、删除、替换外,还可以行为单位进行部分数据的查找替换。
替换命令语法
sed '[address-range|pattern-range] s/original-string/replacement-string/[substitute-flags]' input fileaddress-range或pattern-range(即地址范围或模式范围)是可选的,如果没有指定,那么 sed 将在所有行上进行替换s即执行替换命令 substituteoriginal-string是被 sed 搜索然后被替换的字符串,它可以是一个正则表达式replacement-string是替换后的字符串substitute-flags是可选的
4.7.1、用 you 替换 YOU
Section titled “4.7.1、用 you 替换 YOU”$ sed 's/you/YOU/' myheart1 Every night in my dreams2 I see YOU, I feel you3 That is how I know YOU go on4.7.2、全局标志
Section titled “4.7.2、全局标志”在上面的例子(4.7.1)可以发现第2行中有两个 you 只有第一个被替换成 YOU,因为默认情况下,sed会替换每行中第一个original-string,可以使用
全局标志 g 将每行中所有匹配项都进行替换。
$ sed 's/you/YOU/g' myheart1 Every night in my dreams2 I see YOU, I feel YOU3 That is how I know YOU go on4.7.3、在指定的目标范围进行替换
Section titled “4.7.3、在指定的目标范围进行替换”在4.7.1中,第2、3行中的you 被成功替换成 YOU,如果只想要期中的部分行进行替换,那么可以通过直接指定地址范围
或者模式范围,而不是默认的所有行上,进行替换操作。
指定地址范围
只在第三行进行,将 you 替换 YOU
$ sed '3 s/you/YOU/g' myheart1 Every night in my dreams2 I see you, I feel you3 That is how I know YOU go on指定模式范围
在包含 know 的行,将 you 替换 YOU
$ sed '/know/ s/you/YOU/g' myheart1 Every night in my dreams2 I see you, I feel you3 That is how I know YOU go on4.7.4、指定匹配的original-string次序进行替换
Section titled “4.7.4、指定匹配的original-string次序进行替换”在4.7.1中,第二行中有两个 you 可以匹配到,默认是第一个匹配项被替换,可以通过指定数字标志来指定次序匹配项被替换。
如,要第二个匹配项 you 被替换,可以使用:
$ sed 's/you/YOU/2' myheart1 Every night in my dreams2 I see you, I feel YOU3 That is how I know you go on使用s/you/YOU/2 后,每一行都是第二个匹配项才会被替换,所以第三行的 you 没有被替换。
4.7.5、只打印替换后的行
Section titled “4.7.5、只打印替换后的行”在4.7.4中,将每行中的第二个 you 替换成 YOU, 可以配置 p命令和 -n 选项,将发生替换的行显示出来,而其他行不输出。
$ sed -n 's/you/YOU/2p' myheart2 I see you, I feel YOU4.7.6、忽略大小
Section titled “4.7.6、忽略大小”可以使用i标志,在模式匹配中忽略大小写,进行匹配替换。如可以改写4.7.5,使用yOu进行匹配替换:
sed -n 's/yOu/YOU/2pi' myheart2 I see you, I feel YOU如果没有i标志时,就不会发生更改替换:
$ sed 's/yOu/YOU/2' myheart1 Every night in my dreams2 I see you, I feel you3 That is how I know you go on
$ sed -n 's/yOu/YOU/2p' myheart4.7.7、大小写替换
Section titled “4.7.7、大小写替换”进行大小写替换可以使用命令 y, 该命令根据对应位置进行字符转换:
$ sed 'y/you/YOU/' myheart1 EverY night in mY dreams2 I see YOU, I feel YOU3 That is hOw I knOw YOU gO On将所有的y->Y,o->O,u->U,也可以和4.7.3一样进行指定范围或模式范围进行限定,这样可以避免在所有的行进行操作。
也可以在命令 s 中,使用特殊的功能来实现。如:\L 将匹配文本替换为小写,\l 则只是转换下一个字符为小写,\U 将匹配的文本替换为大写,
\u 将只是将下一个字符转为大写,& 指的是匹配到的模式:
$ gsed 's/you/\U&/' myheart1 Every night in my dreams2 I see YOU, I feel you3 That is how I know YOU go on同样的可以使用 g 标志,将所有匹配项都进行替换:
$ gsed 's/you/\U&/g' myheart1 Every night in my dreams2 I see YOU, I feel YOU3 That is how I know YOU go on4.7.8、每一行后添加一行空白行
Section titled “4.7.8、每一行后添加一行空白行”可以使用 G 命令,也可以是用 s 命令完成,如:
$ sed G myheart1 Every night in my dreams
2 I see you, I feel you
3 That is how I know you go on
$ sed 's/.*/&\n/' myheart1 Every night in my dreams
2 I see you, I feel you
3 That is how I know you go onG 命令把当前保持空间的内容作为新行追加到模式空间,模式空间中的内容不会被覆盖,
该命令在模式空间后面加上换行符 \n,然后保持空间内容添加进去,sed G myheart 中保持空间没有内容,
所以达到仅仅只是换行的目的。
sed 's/.*/&\n/' myheart 使用 s 命令利用正则表达式匹配一整行,并是用 & 表示当期匹配的内容,紧跟
换行符 \n,达到每一行后添加一个换行的目的。
4.8、批量修改文件
Section titled “4.8、批量修改文件”用可选项 -i 可以直接修改原始文件内容,如:
sed -i 's/one/two' demo.txt上面的命令,将文件中demo.txt,每行中的第一个 one 修改成 two。这条命令在Mac 系统中会提示错误(Linux 能正常运行):
sed: 1: "demo.txt": command c expects \ followed by text对于这个问题,StackOverflow有个答案:sed command with -i option failing on Mac, but works on Linux
下面有一个不错的方案:
This works with both GNU and BSD versions of sed:
sed -i'' -e 's/old_link/new_link/g' *or with backup:
sed -i'.bak' -e 's/old_link/new_link/g' *如果没有其他备份机制,使用-i'.bak来保存备份,来避免错误修改且丢失原始文件造成灾难。
回到主题:批量修改文件,可以联合 xargs, find 或 rg 命令来完成。
如当前目录下有大量的 Markdown 文件,需要删除文件中的一行 contentCopyright: true,那么可以使用下面的命令:
$ rg --type md --files-with-matches 'contentCopyright: true' | xargs sed -i'' '/contentCopyright: true/d'首先使用 rg 找到包含 contentCopyright: true 的 Markdown 文件, 然后交给 sed 修改。
--type md指定rg在Markdown文件中进行匹配--files-with-matches选项指定rg只返回匹配项的文件, 不返回匹配的内容详情-i''sed直接修改原始文件内容