Sed 代表 Strem Editor(流编辑器),是操作、过滤和转换文本内容的强大工具。 它最初是为 AT&T 最初的 Unix 操作系统第 7 版创建的,此后可能每一个 Unix 和 Linux 操作系统都包含了它。 sed 以行为处理单位,针对每一行进行处理。 功能上同awk类似,sed功能更简单,针对列处理功能要差很多。
1、sed 命令语法
|
|
可选参数
-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 脚本执行流程
sed脚本执行的步骤很容易记住:读取(Read),执行(Execute),打印(Print),重复(Repeat)。我们可以利用这几个步骤的首字母REPR来记忆sed执行的步骤。
我们来看一下这几个步骤。 sed将会:
- 读取一行内容到模式空间(sed内部的一个临时缓存,用于存放读取到的内容)
- 执行:对模式空间里的内容执行sed命令。 如果使用了 {} 或 -e 指定多个命令,sed将依次执行每个命令
- 打印(输出)模式空间的内容。然后清空模式空间
- 重复以上流程,直到文件结束
3、sed 命令
a
: 追加命令,可以在指定位置后面插入新行i
: 插入命令,在指定位置之前插入新行c
: 修改命令,取代指定位置旧行d
: 删除命令,删除指定行p
: 打印命令,打印当前模式空间的内容,通常和可选参数-n
一同使用s
: 替换命令,进行字符串替换y
: 转换字符,根据对应位置转换字符,如进行大小写转换=
: 打印行号,会在每一行的后显示改行的行号q
: 终止正在执行的命令并退出sed。sed正常执行流程是读取数据、执行命令、打印结果、重复循环。当sed遇到q命令时,便停止执行后续循环立即退出r
: 从指定的文件读取内容,并在指定的位置将其打印出来
4、实例技巧
有一段歌词保存在文件myheart,内容如下:
1 2 3 |
1 Every night in my dreams 2 I see you, I feel you 3 That is how I know you go on |
完整歌词可以在My Heaert Will Go On 获取完成歌词
4.1、追加行
使用 a
命令可以在指定位置后插入新行,语法格式:
|
|
4.1.1、在第一行后追加一行
|
|
在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、在最后一行追加一行
|
|
4.1.3、在匹配行后追加一行
|
|
4.2、新行取代旧行
使用c
命令可以,用给定的行替换指定位置的旧行。
4.2.1、用新数据取代第一行
|
|
将第一行替换为Far across the distance
4.2.2、用新数据替代匹配you
的行
|
|
4.2.3、用多行新数据替代匹配you
的行
|
|
/you/
匹配到了两行,所以这两行都进行了替换
4.2.4、每一行后新增空白行
|
|
G
命令把当前保持空间的内容作为新行追加到模式空间中, 模式空间的内容不会被覆 盖,该命令在模式空间后面加上换行符\n,然后把保持空间内容追加进去。
4.5、删除行
4.5.1、删除第一行
|
|
4.5.2、保留第一行其余都删除
|
|
4.5.2、删除最后一行
|
|
4.5.3、删除匹配行
|
|
4.5.4 删除2开头的行
|
|
4.5.5 删除从第一次匹配行到最后一行
|
|
4.5.6 删除从第一次匹配行和它后面的一行
|
|
4.5.7 删除从第一次匹配行到第二行
|
|
4.6、搜索显示数据
4.6.1、显示第一行
sed在执行完成命令后会默认打印模式空间的内容,而命令p
也会输出当前模式空间的内容。所以,
通常使用命令p
时,还需要使用-n
选项来屏蔽sed的默认输出,否则使用p命令后,每行记录会输出两次。
|
|
4.6.2、显示最后一行
|
|
4.6.3、显示第二到第三行
|
|
4.6.4、搜索显示匹配的行
|
|
显示包含Every
的行
4.7、 替换数据
sed除了整行的新增、删除、替换外,还可以行为单位进行部分数据的查找替换。
替换命令语法
1
|
sed '[address-range|pattern-range] s/original-string/replacement-string/[substitute-flags]' input file |
address-range
或pattern-range
(即地址范围或模式范围)是可选的,如果没有指定,那么 sed 将在所有行上进行替换s
即执行替换命令 substituteoriginal-string
是被 sed 搜索然后被替换的字符串,它可以是一个正则表达式replacement-string
是替换后的字符串substitute-flags
是可选的
4.7.1、用 you 替换 YOU
|
|
4.7.2、全局标志
在上面的例子(4.7.1
)可以发现第2行中有两个 you
只有第一个被替换成 YOU
,因为默认情况下,sed会替换每行中第一个original-string
,可以使用
全局标志 g
将每行中所有匹配项都进行替换。
|
|
4.7.3、在指定的目标范围进行替换
在4.7.1
中,第2、3行中的you
被成功替换成 YOU
,如果只想要期中的部分行进行替换,那么可以通过直接指定地址范围
或者模式范围,而不是默认的所有行上,进行替换操作。
指定地址范围
只在第三行进行,将 you
替换 YOU
|
|
指定模式范围
在包含 know
的行,将 you
替换 YOU
|
|
4.7.4、指定匹配的original-string
次序进行替换
在4.7.1
中,第二行中有两个 you
可以匹配到,默认是第一个匹配项被替换,可以通过指定数字标志来指定次序匹配项被替换。
如,要第二个匹配项 you
被替换,可以使用:
|
|
使用s/you/YOU/2
后,每一行都是第二个匹配项才会被替换,所以第三行的 you
没有被替换。
4.7.5、只打印替换后的行
在4.7.4
中,将每行中的第二个 you
替换成 YOU
, 可以配置 p
命令和 -n
选项,将发生替换的行显示出来,而其他行不输出。
|
|
4.7.6、忽略大小
可以使用i
标志,在模式匹配中忽略大小写,进行匹配替换。如可以改写4.7.5
,使用yOu
进行匹配替换:
|
|
如果没有i
标志时,就不会发生更改替换:
|
|
4.7.7、大小写替换
进行大小写替换可以使用命令 y
, 该命令根据对应位置进行字符转换:
|
|
将所有的y
->Y
,o
->O
,u
->U
,也可以和4.7.3
一样进行指定范围或模式范围进行限定,这样可以避免在所有的行进行操作。
也可以在命令 s
中,使用特殊的功能来实现。如:\L
将匹配文本替换为小写,\l
则只是转换下一个字符为小写,\U
将匹配的文本替换为大写,
\u
将只是将下一个字符转为大写,&
指的是匹配到的模式:
|
|
同样的可以使用 g
标志,将所有匹配项都进行替换:
|
|
4.7.8、每一行后添加一行空白行
可以使用 G
命令,也可以是用 s
命令完成,如:
|
|
G
命令把当前保持空间的内容作为新行追加到模式空间,模式空间中的内容不会被覆盖,
该命令在模式空间后面加上换行符 \n
,然后保持空间内容添加进去,sed G myheart
中保持空间没有内容,
所以达到仅仅只是换行的目的。
sed 's/.*/&\n/' myheart
使用 s
命令利用正则表达式匹配一整行,并是用 &
表示当期匹配的内容,紧跟
换行符 \n
,达到每一行后添加一个换行的目的。
4.8、批量修改文件
用可选项 -i
可以直接修改原始文件内容,如:
1
|
sed -i 's/one/two' demo.txt |
上面的命令,将文件中demo.txt,每行中的第一个 one
修改成 two
。这条命令在Mac 系统中会提示错误(Linux 能正常运行):
1
|
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:
1
sed -i'' -e 's/old_link/new_link/g' *or with backup:
1
sed -i'.bak' -e 's/old_link/new_link/g' *
如果没有其他备份机制,使用-i'.bak
来保存备份,来避免错误修改且丢失原始文件造成灾难。
回到主题:批量修改文件,可以联合 xargs
, find
或 rg
命令来完成。
如当前目录下有大量的 Markdown
文件,需要删除文件中的一行 contentCopyright: true
,那么可以使用下面的命令:
|
|
首先使用 rg
找到包含 contentCopyright: true
的 Markdown
文件, 然后交给 sed
修改。
--type md
指定rg
在Markdown文件中进行匹配--files-with-matches
选项指定rg
只返回匹配项的文件, 不返回匹配的内容详情-i''
sed
直接修改原始文件内容