资讯详情

资讯详情

SFTP put() 随机 ENOENT:Git Bash 悄悄把 /opt 改写成了 C 盘路径

三个同名 jar 和一条走不通的 SFTP 通道项目里三个子目录各放一个 jar包名完全相同。我写了个 ssh134.py用 paramiko 做 SFTP 助手负责探查服务器和上传部署脚本。第一版跑起来mkdirs()成功紧接着put()抛ENOENT。重试五次三次失败。把同一逻辑拆成file.open()手动分块写入反而稳定通过。两条路径走的是同一个 SFTP 连接行为却完全不同。两个中间坑通道复用和 setpipeline翻 ssh134.py 源码发现mkdirs和upload各自调了一次self.ssh.open_sftp()等于对同一台服务器开了两条 SFTP 通道。第二通道在 paramiko 里生命周期管理比较脆弱没做连接池复用时直接崩。改成复用同一条通道后逐步复现全部成功。但再跑一遍 ssh134.py又稳定失败用python -c内联执行同一段代码又稳定成功。差异不在代码逻辑在执行环境。排查过程中还发现一个 API 兼容问题SFTPFile.setpipeline()在 paramiko 5.0 里已经不存在了。旧脚本里留着这个调用抛的是AttributeError而不是预期的ENOENT一度把排查方向带偏。grep 了一遍项目里所有SFTPFile方法调用把 5.0 已砍掉的旧 API 全清掉。另一个小坑脚本里部分路径写的是相对路径opt/apps/...Git Bash 下会被拼到当前工作目录。改成强制绝对路径/opt/apps/...后路径解析这一环不再引入额外变量。真凶MSYS 把 /opt 改写成了 C 盘ssh134.py 稳定失败、inline 稳定成功——这个对比指向 shell 环境。我在put()前加了一行print把实际传给 SFTP 的远程路径打出来输出是C:/Program Files/Git/opt/apps/xxx-a/app.jar不是/opt/apps/...是 Windows 路径。Git Bash 底层的 MSYS2 默认开启参数转换看到以/开头的命令行参数就认为它是本地 Unix 路径自动前缀C:/Program Files/Git/。服务器端收到这个路径自然返回ENOENT。解法一行exportMSYS2_ARG_CONV_EXCL*我在 ssh134.py 里加了一层防御性路径还原——检测传入路径是否被注入了C:/Program Files/Git前缀是就 strip 掉。这样不管在 Git Bash 还是 cmd 里跑都不会再踩。可带走的三条经验这次从偶发 ENOENT到定位 MSYS 路径转换关键转折点是那组对比实验脚本执行 → 稳定失败同逻辑 inline → 稳定成功把远程路径 print 出来 → 看到 Windows 前缀。三个事实一拼根因锁死。SFTP 操作如果偶发失败优先检查是否每次操作都开了新通道改成复用单通道能消掉一类问题。升级 paramiko 大版本前grep 项目里所有 SFTP 相关方法调用5.0 砍掉了一批旧 APIsetpipeline就是典型。在 Git Bash 里跑涉及/xxx路径参数的脚本前先echo $1看看参数有没有被 MSYS 改写生产脚本里加MSYS2_ARG_CONV_EXCL*或做路径防御还原。排查顺序比工具更重要。如果一开始就钻进 paramiko 源码翻大概率绕更久把什么条件下成功、什么条件下失败这个变量隔离出来根因就跳出来了。做自动化这几年最耗时间的从来不是写代码是摸清每个平台的脾气。这篇里提到的坑都是真金白银踩出来的。如果你手上也有重复度很高的活儿——批量发布、数据搬运、有固定规则的机械操作——可以在评论区说说你的场景我看看能不能自动化掉。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →