DEBUG.WIKI
京东云;让 AI 进入真实业务;零售 · 物流 · 客服 · 硬件;查看方案 →
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国
Evoxt;最高 6.0GHz CPU;高频云服务器 · 免费每周备份;查看配置 →

GNU sed -i 使用输入重定向时报 no input files

输入重定向只连接标准输入,不会传入原地编辑所需的文件路径。区分查看转换输出和保存回文件,修正参数并保留可恢复备份。

运行 sed -i 's/old/new/g' < ./app.conf,文件明明存在,却收到 sed: no input files。这里的输入内容确实接到了标准输入,但 GNU sed 的原地编辑需要命令行里的文件路径;shell 的 < 重定向没有把路径作为参数传进去。

本文针对 GNU sed 的 -i,不把 BSD sed 等实现的选项语法混用。普通的流式替换和原地编辑有不同的输入要求,应先确定你希望得到输出,还是改写那个文件。

先去掉 -i,确认读入和替换表达式

只查看转换后的内容,可以使用:

sed 's/old/new/g' < ./app.conf

app.conf、old、new 都是示例,须换成实际文件和经过核对的表达式。这条命令把结果写到标准输出,不会保存回输入文件。它适合检查匹配范围,但不要把含凭据的配置内容直接输出到公共日志。

如果这种形式能读到内容,而增加 -i 后出现 no input files,应检查参数中是否缺少文件名。此错误不等于文件权限不足,增加 sudo 也不会把标准输入变成文件参数。

< ./app.conf 由 shell 打开文件并连接输入。sed 收到的是一个可读取的输入流;它没有从参数中获得要替换的路径。把同一个文件改用 cat 管道送入,也没有解决原地编辑缺少路径的问题。

要保存回文件,把路径放在参数位置

先保留独立可恢复副本,确认没有其他进程同时更新该文件,再使用 GNU sed 的带备份后缀形式:

sed -i.before-change 's/old/new/g' ./app.conf

关键变化是删除文件名前的 <,让 ./app.conf 成为输入文件参数。这里的 .before-change 是备份后缀,编辑前应确认 ./app.conf.before-change 没有需要保留的旧内容,避免覆盖已有备份。

GNU sed 的原地编辑会通过临时文件替换路径,不能据此认为 inode、硬链接或符号链接关系都保持原样。若目标是链接,先决定应修改哪个实际文件;不要因为这条命令能运行,就跳过文件关系检查。

编辑后比较新文件和此次备份,确认只修改预期内容。服务配置还需通过相应程序的配置检查;磁盘上的文本变化不代表服务已经重新加载。

循环中也要传文件参数

如果脚本已经把一个经过筛选的路径保存在 file 中,调用应写成:

sed -i.before-change 's/old/new/g' -- "$file"

双引号让包含空格的路径仍是一个参数;-- 结束选项解析,避免以连字符开头的文件名被当作选项。这里假设 file 已经指向本轮要编辑的普通文件,且备份和修改范围已经确认;这一行本身不会筛选目录或验证链接。

不要把文件列表用 $(find ...) 再按空白拆开。批量编辑还应分别确认目录、链接、文件名中的空白及每个文件的处理结果。一次调用中途失败不保证此前文件没有改变,所以不能仅修好脚本后直接重复整批写入。

输入流只能另存,不能靠重定向覆盖原文件

如果任务本来就是处理管道内容,去掉 -i,把结果保存到一个独立、未占用的输出路径即可。不要写成 sed ... < ./app.conf > ./app.conf:shell 可能先截断输出路径,导致 sed 读到已经被清空的文件。

需要回退时,先保存当前文件,再核对备份确实对应本次修改前的版本,按原路径恢复并重新检查配置。不要通过把 new 全部替换回 old 来回退,那会连原本合法的 new 一起修改。