你写好了一个部署脚本,准备把打包好的文件通过 SFTP 传到 NAS 上,再去目标机器安装。结果第一步就卡住了:连往临时目录写文件都失败,报的还是"文件不存在"。你 ls 一下,目录明明就在那里,看得见、进得去,可就是写不进去。
现象
- 用 SFTP 往 NAS 上传文件,连临时目录都写不进去;
- 报错信息说文件不存在(File not found / No such file);
- 但用同一个连接列目录却完全正常,目录真实存在。
"目录存在,却跟你说文件不存在"——这个矛盾本身就是线索:它在用"不存在"作为拒绝的方式。
根因
根因是:这台 NAS 的 SFTP 子系统禁止写入,而它拒绝写入时给出的错误是"文件不存在",而不是"权限被拒"。
很多 NAS 设备的 SFTP(SSH 文件传输子系统)是按只读设计的。它们面向的是"浏览、下载、备份读取"这类场景,出于安全考虑(防止通过 SFTP 往系统里塞文件)直接把写操作禁掉了。当你的写请求进来,它没法真的创建一个文件,于是回了一个语义上最接近"这里没有你要操作的东西"的错误——文件不存在。
这属于错误信息误导:真实原因(不允许写)和报出来的信息(不存在)指向了完全不同的排查方向。如果你顺着"路径是不是写错了""文件名有没有问题"去查,会一直查不到点,因为路径和文件名都是对的。
要区分清楚两种"写不进去":
- 文件权限不足:目录存在但没有写权限,报的是
Permission denied; - 功能被禁用:整个 SFTP 写通道被关掉,报的可能是"文件不存在"这类含糊信息。
这台 NAS 属于后者。
解决
思路:绕开被禁用的 SFTP 写通道,改用 SSH 命令通道落盘。
SFTP 只是 SSH 的一个子系统。写不了,不代表整条 SSH 通道不能用。 只要 SSH 能执行命令,就可以用命令把内容写进去。一条常用做法是:把内容 base64 编码后,经 SSH 命令通道解码落盘——base64 只用 ASCII 字符,能安全穿过命令行的引号、换行等转义问题。
# 1. 本地把文件内容 base64 成一行
B64=$(base64 -w0 ./deploy.tar.gz)
# 2. 经 SSH 命令通道传到远端并解码落盘
ssh <用户>@<IP> "echo '$B64' | base64 -d > /tmp/deploy.tar.gz"
# 3. 校验两端哈希一致(重要)
# 本地
sha256sum ./deploy.tar.gz
# 远端
ssh <用户>@<IP> "sha256sum /tmp/deploy.tar.gz"
落在临时目录后,再用 sudo 安装到目标位置:
ssh -t <用户>@<IP> "sudo mv /tmp/deploy.tar.gz /目标目录/"
几个要点:
- base64 用
-w0,去掉换行,否则一行拆成多行会在远端echo时出问题。 - 内容很大时,命令行长度有限制,base64 传输适合中小文件;大文件考虑用
scp/rsync(如果它们可用)或分段传输。 - 务必校验哈希。传输 + 解码 + shell 转义,任何一步出错都可能让文件静默损坏。
sha256sum两端一致,才算真的到齐了。
如果远端连 SSH 命令通道都受限,那就只能换传输方式(U 盘、HTTP 上传接口等),但多数 NAS 至少保留了 SSH 命令执行能力。
延伸与预防
这条坑的通用教训是:报"不存在"的错误,也可能是"不允许"。
排查时不要被错误信息的字面意思牵着走。当"目录明明存在却说文件不存在"这类矛盾出现时,先怀疑权限或功能限制,而不是路径拼写:
- 换个角度验证。
ls能列、write不能写,指向的就是"只读限制",而不是路径问题。 - 换通道验证。SFTP 不行时试试 SSH 命令执行、
scp、HTTP 接口——一个通道被关,往往还有别的通道开着。 - 区分"权限位"和"功能开关"。两者都表现为"拒绝",但一个改权限位能解,另一个得换方法。
一句话:当错误信息和事实矛盾时,相信事实,重新推断错误信息真正的含义。