
sed流编辑器小笔记(下)
使用
创建
file.txt,作为下面示例的输入。后面的命令默认独立演示,直接修改文件后,可重新执行下面的命令恢复示例。
1 | cat > file.txt <<'EOF' |
1.在行前或行后插入内容
行前插入
1 | sed '/hello/i\ |
1 | old_text old_text |
行后追加
1 | sed '/hello/a\ |
1 | old_text old_text |
/hello/选择包含hello的行,也可以换成行号i在行前插入,a在行后追加- 匹配多行时,每个匹配行都会执行一次
💡提示
GNU sed还支持
sed '/hello/i new_line' file.txt这样的单行写法。这里使用带实际换行的形式,兼顾macOS自带的BSD sed。
2.替换整行内容
1 | sed '/hello/c\ |
1 | old_text old_text |
c替换选中的整行,s替换行中匹配到的字符串,两者不要混淆。
3.修改源文件
直接修改
1 | # GNU sed |
上述两种写法选择一种即可。命令正常完成时没有终端输出,可以通过cat file.txt查看修改结果。
先备份后修改
1 | sed -i.bak 's/old_text/new_text/g' file.txt |
执行后得到两个文件。
1 | file.txt 修改后的文件 |
💡提示
建议先不加
-i预览结果,再使用-i.bak修改。重复使用同一个备份后缀时,旧备份可能被覆盖。不要使用
sed '命令' file.txt > file.txt,Shell会先清空输出文件,sed可能读不到原来的内容。
4.从文件读取内容
1 | printf '%s\n' 'read from another file' > extra.txt |
1 | old_text old_text |
r extra.txt读取另一个文件,并在匹配行后输出它的内容。匹配多行时,会插入多次。
5.写入文件
1 | sed -n '/123/w result.txt' file.txt |
1 | 123456 abcde |
w result.txt把选中的模式空间内容写入文件-n关闭默认输出,所以sed本身不会在屏幕上显示这些行w不是Shell重定向;同一次运行中,多次匹配的结果会依次写入,原有目标文件内容会被覆盖
6.字符转换
1 | sed '1,2y/abcde/ABCDE/' file.txt |
1 | olD_tExt olD_tExt |
只转换第1~2行,a对应A、b对应B,依此类推。
y按字符逐个转换,不使用正则表达式;两边的字符数量必须相同。
7.打印奇偶行
打印奇数行
1 | sed -n 'p;n' file.txt |
1 | old_text old_text |
打印偶数行
1 | sed -n 'n;p' file.txt |
1 | hello shell |
这里的n读取当前输入的下一行,替换模式空间,再从下一条命令继续执行;它不会自动读取另一个文件。
GNU sed也支持步长地址。
1 | sed -n '1~2p' file.txt # 从第1行开始,每隔2行选中一行 |
8.保持空间
除了模式空间,sed还有一个用于暂存内容的保持空间(Hold Space)。
这张图是内容的传递方向,不是处理循环;只有执行对应命令时,两个空间之间才会传递内容。x则直接交换两边的内容。
| 指令 | 说明 |
|---|---|
h |
将模式空间复制到保持空间,覆盖原有内容 |
H |
在保持空间后追加换行符和模式空间内容 |
g |
将保持空间复制到模式空间,覆盖原有内容 |
G |
在模式空间后追加换行符和保持空间内容 |
x |
交换两个空间的内容 |
举个例子
1 | sed -e '/123/h' -e '$G' file.txt |
1 | old_text old_text |
- 遇到包含
123的行时,h将这一行复制到保持空间 - 遇到最后一行时,
G将保存的内容追加到模式空间 - 最后默认输出模式空间,因此看起来像在文件末尾追加了一行;原文件没有被修改
💡提示
多行匹配
/123/时,后一次h会覆盖前一次,最终只追加最后一个匹配行。可以把它理解为只有一个格子的剪贴板,不是历史记录列表。如果一直没有匹配,保持空间为空,
G仍会追加一个换行符,输出中会多出一个空行。
总结
(1)i、a、c分别用于行前插入、行后追加、替换整行
(2)r和w用于读写文件,-i用于直接修改输入文件
(3)模式空间用于当前处理,保持空间用于跨处理轮次保存内容
(4)小写h、g是覆盖,大写H、G是追加
练习
基础
先复制一份系统用户文件,在副本上练习。
1 | cp /etc/passwd user.txt |
系统用户信息因环境而异,下面只列出命令,不写固定输出。除第13题外,这些命令只输出处理结果。
1.打印所有行
1 | sed -n 'p' user.txt |
2.打印第1~8行
1 | sed -n '1,8p' user.txt |
3.打印包含bash的行
1 | sed -n '/bash/p' user.txt |
4.删除第6行及其后面的所有行
1 | sed '6,$d' user.txt |
5.打印包含nologin的行
1 | sed -n '/nologin/p' user.txt |
6.将bash替换成zsh,显示替换后的相关行
1 | sed 's/bash/zsh/g' user.txt | grep 'zsh' |
7.将/sbin/nologin替换成/bin/login
1 | sed 's#/sbin/nologin#/bin/login#g' user.txt | grep 'login' |
路径中本来就有/,换成#作为替换命令的分隔符,可以少写一些转义符。
8.输出行号和内容
1 | sed -n '=;p' user.txt |
行号和内容会分别占一行。
9.删除第5~10行中的数字,并只输出这一范围
1 | sed -n -e '5,10s/[0-9]//g' -e '5,10p' user.txt |
10.只保留ASCII数字和大小写字母,输出第6~14行
1 | LC_ALL=C sed -n -e 's/[^0-9a-zA-Z]//g' -e '6,14p' user.txt |
这里也会删除空格、冒号和下划线,并不是只删除标点。
11.交换第一个字段和最后一个字段,输出最后5行
1 | sed -E 's/^([^:]*):(.*):([^:]*)$/\3:\2:\1/' user.txt | tail -n 5 |
passwd文件使用冒号分隔字段。注意,^(.)(.*)(.)$交换的是首尾字符;交换字段时,需要匹配到分隔符的位置。
12.给日志的第4行及以后加上待检查标记,输出第2~6行
1 | cat > deploy.log <<'EOF' |
1 | INFO build complete |
使用$在行尾追加;.$会连同最后一个字符一起替换。这里仅给输出加标记,方便排查,不修改原日志。
13.修改本地配置中的端口,直接修改并备份
1 | printf '%s\n' 'APP_PORT=3000' 'APP_HOST=127.0.0.1' > app.env |
1 | APP_PORT=8080 |
app.env.bak保留修改前的配置。这条命令只修改文本,应用是否读取新端口还要看启动方式。
14.显示奇数行
1 | sed -n 'p;n' user.txt |
15.显示偶数行
1 | sed -n 'n;p' user.txt |
小应用
1.提取最后一个目录名
1 | echo '/etc/rc.d/init.d/' | sed -E 's#/*$##;s#^.*/##' |
1 | init.d |
- 第一条命令去掉末尾的斜线
- 第二条命令去掉最后一个斜线及其前面的内容
- 对
/etc/rc.d/init.d也可以使用;如果输入只有根目录/,结果会为空
2.修改应用日志级别
开发时开着调试日志很方便,处理完问题后,可以把本地配置中的日志级别改回来。
1 | cat > app-demo.conf <<'EOF' |
1 | # LOG_LEVEL=DEBUG |
^LOG_LEVEL=锚定行首,避免修改注释或其他配置项。这里只修改文件,是否需要重启或重新加载配置,取决于应用本身。
日志可以少一点,备份还是要有的。毕竟“我就改一行”也是不少事故故事的开场白。
3.提取网卡的IPv4地址
Linux中可以使用ip命令,避免依赖ifconfig输出的固定行号。
1 | ip -4 -o addr show dev ens33 | sed -nE 's/.* inet ([0-9.]+)\/.*/\1/p' |
ens33需要替换为实际网卡名称。下面用模拟输入演示同一个表达式。
1 | printf '%s\n' '2: ens33 inet 192.168.10.20/24 brd 192.168.10.255 scope global ens33' | sed -nE 's/.* inet ([0-9.]+)\/.*/\1/p' |
1 | 192.168.10.20 |
一个网卡存在多个IPv4地址时,会分别输出。








