一条在脚本里用了多年的 scp 命令,可能在当前客户端上失败,报出的路径错误与隧道毫无关系。VPN 把字节送到了;客户端却向服务器要了一个服务器无法解析的名字。当前 OpenSSH 的 scp 默认通过 SFTP 传输,而 SFTP 不会像旧协议那样做 shell 风格的展开。从你自己的账户出发向外追这条命令,就能看出路径究竟在哪里被解释。

先从账户出发,再从它能写入的目录出发

冒号分隔两个命名空间,只有左边属于你正在输入的机器。其后的内容全部由服务器在你通过身份验证的账户下、在该账户拥有的文件系统视图内解析。在拼命令之前先确认这两点,因为对管理员正确的路径,不会自动对传输用户也正确。这个差别正是大多数看起来像网络故障的权限错误的来源。

向服务器询问它自己的视图:ssh [email protected] pwd 和 ssh [email protected] ls -ld /srv/archive。如果第二条命令报告权限被拒绝,隧道没有问题,而该路径对这个账户不可用。修正所有权或换一个目标,而不是为了让传输继续而放宽权限。

受限的 SFTP 配置可能把 chroot 呈现为文件系统的根。在那个视图里,绝对路径是 chroot 内的绝对路径,与管理员在主机上看到的位置并不相同。

明确写出冒号的两侧

上传应当写明远程文件名,而不是假设本地基本名在对面也会被接受。

scp q3-invoice.csv [email protected]:/home/transfer/drop/q3-invoice.csv

下载也应当出于完全相同的理由写明本地目标。两个命名空间彼此独立,假设它们一致的脚本,会在远程树第一次采用不同布局时出错。

scp [email protected]:/srv/archive/2026-q3.csv ./2026-q3.csv

在需要维护的脚本里避免使用 ~otheruser。展开另一个账户的家目录曾是远程 shell 的功能,SFTP 服务器未必实现模拟它的扩展。写出该账户真正能打开的绝对路径,并对含空格的本地路径加引号,而不是照搬为服务器上的 shell 写的引号层。

弄清你的 scp 实际使用哪种协议

自 OpenSSH 9.0 起,客户端默认使用 SFTP,各发行版按自己的节奏采纳或向后移植该行为。版本字符串说明了客户端的意图,但厂商构建可能不同于上游,所以要检查你实际运行的客户端:ssh -V。

两种协议解析名称的方式不同。旧 SCP 让远程用户 shell 去展开路径,因此通配符和波浪号由那个 shell 按自己的规则处理。SFTP 则发出文件传输操作,因此通配符要么由客户端展开,要么完全不展开。两者都在 SSH 内到达同一台机器,这就是同一台主机在两种客户端下表现不同的原因。

scp -v 会打印它请求的子系统,而 sftp 子系统行确认正在使用现代路径。scp -O 在受支持且确实需要的地方强制使用旧协议,它也把自身的引号规则带了回来。它不是 VPN 选项,不是在服务器上启用什么的方法,也不是放宽主机密钥检查的理由。

在脚本依赖这条路径之前先测试它

路径问题往往在最糟的时刻暴露:夜里无人看管的自动化任务。先用一个小文件和一个该任务实际使用的账户验证目标,并把那条测试命令放在任务定义旁边。如果测试需要另一个账户或另一个目录,任务也会需要。

也要检查服务器一侧。grep -i subsystem /etc/ssh/sshd_config 显示是否配置了 SFTP 子系统,而 sftp -b /dev/null [email protected] 只有在该子系统应答时才会成功退出。一台接受 SSH 登录却没有子系统的主机,会在每次现代传输中失败,尽管在登录测试里看起来完全健康。

把客户端版本、所用协议和绝对远程路径记进任务备注。当下一次升级改变某个默认值时,那条备注就是五分钟修复与一下午猜测之间的差别。

相关文章