三种需求:全删空行、连空白行也删、压连续空行。每种一行命令搞定。
全删空行:sed 与 grep
只删真正空的行(^$,行里一个字符都没有):
sed '/^$/d' input.txt
grep -v '^$' input.txt
sed '/^$/d' 读一行、匹配 ^$ 就丢弃,其余原样输出[1]。grep -v '^$' 反向保留非空行[2]。两者结果一样,差别是 sed 能原地改文件,grep 默认只打到标准输出。
连空白行也删
含空格、Tab 的「空白行」也算空行时,用字符类 [[:space:]]:
sed '/^[[:space:]]*$/d' input.txt
awk 'NF' input.txt
grep -v '^[[:space:]]*$' input.txt
[[:space:]] 匹配空格、Tab、回车等空白字符,* 表示零个或多个,整行只有空白就删[1]。awk 'NF' 靠字段数判断:默认按空白分字段,空白行字段数为 0,条件为假就不打印,效果和上一行相同[3]。
压缩连续空行
只想把多行连空压成一行,保留一个空行分隔段落,用 cat -s:
cat -s input.txt
cat -s 把连续的空行压成一行[4]。注意它只认真正空行,遇到「只有空格」的行会打断连续空行的计数,那行会被保留。
原地修改与备份
sed -i 直接改文件,加后缀自动留备份:
sed -i '/^[[:space:]]*$/d' input.txt
sed -i.bak '/^[[:space:]]*$/d' input.txt
第一行无备份;第二行生成 input.txt.bak,改坏了用 mv input.txt.bak input.txt 回滚。grep/awk/cat 没有原地选项,要改文件得重定向到临时文件再替换:
grep -v '^[[:space:]]*$' input.txt > input.txt.tmp && mv input.txt.tmp input.txt
排错速查
| 现象 | 原因 | 处理 |
|---|---|---|
| 空行删不掉 | 行里有不可见空白(空格/Tab) | 换 [[:space:]]* 版本 |
sed: -i may not be used with stdin |
没给文件名,从管道读入 | 先落盘再 sed -i,或用 sed ... > out |
| 改完文件末尾多一行/少一行 | CRLF 行尾(\r)让 ^$ 匹配失败 |
先 dos2unix 或用 [[:space:]] |
cat -s 没压成一行 |
空行之间夹了空白行 | 先用 sed 删空白行,再 cat -s |
实测与局限性
- 实测环境:WSL2 Ubuntu 24.04,GNU sed 4.9、grep 3.11、gawk 5.2.1、coreutils 9.4。
- 已验证:
sed '/^$/d'、sed '/^[[:space:]]*$/d'、grep -v '^$'、grep -v '^[[:space:]]*$'、awk 'NF'、awk 'length($0)>0'、cat -s、sed -i、sed -i.bak的输出与备份文件生成。 - 局限性:WSL2 无法验证 macOS/BSD 的
sed -i语法(BSD 要sed -i '');CRLF 文件需先转换;cat -s只压真正空行。
参考资料
- [1] sed(1) – Linux manual page (man7.org)(man7-sed)
- [2] grep(1) – Linux manual page (man7.org)(man7-grep)
- [3] gawk(1) — gawk — Debian trixie(debian-gawk)
- [4] cat(1) – Linux manual page (man7.org)(man7-cat)
文中主要命令已在 WSL Ubuntu(24.04)实测并核对其在 Debian 系与通用发行版的文档口径。