GNU sed 同时使用 -n 和 -i 后文件变空
GNU sed 的 -n 关闭自动打印,-i 将输出写回原文件;删行脚本没有打印剩余行时会得到空文件。先预览完整输出、保留独立备份,再修正选项。
用 sed 删除某一行,屏幕预览正确,加上原地编辑选项后,整个文件却变成空文件。若实际命令同时带了 -n 和 -i,先检查输出规则:-n 关闭自动打印,-i 则用处理输出替换原文件。没有任何打印动作时,保留下来的行也不会写回去。
下面针对 GNU sed,示例只使用测试目录内的普通文本文件。BSD sed 等实现的原地编辑参数写法不同,不应直接照搬。文件已经为空时,先停止继续编辑,检查备份、快照或版本记录;修改命令不能让已丢失的内容重新出现。
删除匹配行后,其他行仍需要输出
假设独立测试文件 ./records.txt 原来有三行:
KEEP_A
REMOVE
KEEP_B
先运行只输出、不修改输入的命令:
sed '/^REMOVE$/d' ./records.txt
d 删除匹配行的模式空间,立即开始下一轮处理。其他行没有被删除,正常到达本轮结尾,sed 自动打印它们,因此预期输出是 KEEP_A 和 KEEP_B。
再看这个同样不会修改输入的诊断命令:
sed -n '/^REMOVE$/d' ./records.txt
它预期没有输出。匹配行被删除,未匹配行虽然保留在模式空间中,却因为 -n 而不再自动打印。问题不在于正则表达式突然匹配了全部行。
若把这一组合用于原地编辑,GNU sed 会把空的处理输出保存为原文件的新内容。-n 不是“不显示进度”的选项;不能为了让终端安静就加到已有的原地编辑脚本里。
先确认完整输出,再决定是否替换
对于“删除匹配行、保留其余全部行”的目标,通常去掉 -n 就够了。不要同时改正则、路径和输出选项,否则很难确认哪一项改变了结果。
保留 -n 也可以实现同样筛选,但必须明确打印需要的行:
sed -n '/^REMOVE$/!p' ./records.txt
这里 ! 表示不匹配该地址的行,p 将它打印出来。这个例子用于解释输出规则;简单删行任务直接使用前面的 d 写法即可。
不能把“随便补一个 p”当作所有脚本的修复。比如 sed -n 's/old/new/p' 只打印成功发生替换的行,未改动行仍会消失;去掉 -n 后再保留无条件 p,又可能让一行输出两次。应根据整个脚本检查哪些行有输出,而不是只看某一条替换命令。
正则里的 ^ 和 $ 将示例限定为整行 REMOVE。真实任务若从变量取得匹配内容,变量中的正则特殊字符仍有含义;给 shell 变量加引号并不会把内容自动变成字面量正则。先用副本确认匹配范围,不要把示例表达式直接改成未经处理的外部输入。
原地编辑前保留可用的原件
只有确认输入完整、只读预览符合要求,并已有独立备份后,才考虑原地编辑。以下展示 GNU sed 的备份后缀写法,仍只用于自己的测试文件:
sed -i.before-delete '/^REMOVE$/d' ./records.txt
它为旧内容使用 ./records.txt.before-delete 这个备份路径,再将处理结果放到原路径。执行前必须确认备份路径未被占用;固定后缀并不是每次自动创建一个新版本,不能重复运行后仍指望该路径保留最初原件。重要文件应另外保存经过核对的独立备份,不把这一个后缀文件作为唯一恢复依据。
GNU sed 的 -i 后缀紧接选项。将其他短选项接在 -i 后面,可能被解释为备份后缀,不能只凭看起来相似的参数顺序推断效果。
-i 通过临时文件和路径替换完成编辑,不承诺保留硬链接、符号链接关系或所有文件属性。这里限定普通测试文件;涉及应用配置、共享文件或并发写入时,应使用对应的备份、校验和部署流程,避免编辑期间其他进程仍在改同一份内容。
不要把预览改成 sed ... records.txt > records.txt。那会引入另一个问题:shell 可能在 sed 读取前就截断输入文件。预览留在标准输出,或保存到经过确认的独立新路径,再检查内容。
文件已经为空时,恢复内容优先于重跑命令
先核对文件的实际字节数及备份内容,区分零字节与只是第一屏空白。备份存在也要确认它是在错误操作之前生成的,而不是后来又对空文件运行命令得到的副本。
从可信备份恢复到独立位置,检查内容、权限和需要保留的属性,再按原项目流程恢复目标。没有备份且数据不能重新生成时,应保留现状评估恢复可能;sed 本身没有撤销上一轮原地编辑的功能。不要反复执行“修好后的命令”覆盖现场。
验收时核对应删除的行、应保留的行、顺序和重复次数。命令成功退出只说明处理流程未报告错误,不证明输出符合业务要求;全文件被筛空也可能是一次正常结束的执行。对于需要由程序读取的配置,还应使用该程序支持的只读校验方式,再安排加载。


