news 2026/10/28 7:14:53

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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 砍掉了一批旧 API(setpipeline就是典型)。
  • 在 Git Bash 里跑涉及/xxx路径参数的脚本前,先echo "$1"看看参数有没有被 MSYS 改写;生产脚本里加MSYS2_ARG_CONV_EXCL="*"或做路径防御还原。

排查顺序比工具更重要。如果一开始就钻进 paramiko 源码翻,大概率绕更久;把"什么条件下成功、什么条件下失败"这个变量隔离出来,根因就跳出来了。


做自动化这几年,最耗时间的从来不是写代码,是摸清每个平台的脾气。

这篇里提到的坑,都是真金白银踩出来的。
如果你手上也有重复度很高的活儿——批量发布、数据搬运、有固定规则的机械操作
——可以在评论区说说你的场景,我看看能不能自动化掉。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/28 7:14:06

T3 Stack实战:用Next.js、tRPC、Prisma打造端到端类型安全的全栈应用

上个月我在重构一个内部工具时,被前后端联调的类型问题折磨得够呛。后端接口返回的字段改了名,前端编译期毫无反应,只有运行到那一页才发现数据渲染不出来。后来我把整套代码迁移到 t3code 这个项目模板上,问题算是从根上解决了。…

作者头像 李华
网站建设 2026/10/28 7:14:04

从零构建t3code:基于Tauri的本地代码片段生成工具实践

我花了差不多一个月的晚上,把t3code这个工具从想法变成了我能天天用的东西。起因特别简单:我发现自己总是复制粘贴同一段代码——新建一个 REST 接口、初始化一个 Vue 组件、写一个带分页的列表查询。每次都要去翻旧项目,找到差不多的写法&am…

作者头像 李华
网站建设 2026/10/28 7:13:09

Flutter与OpenHarmony音乐播放器专辑详情开发指南

1. 项目背景与需求分析在移动应用开发领域,音乐播放器始终是一个经典且具有挑战性的项目类型。随着Flutter框架的不断成熟和OpenHarmony操作系统的崛起,将两者结合开发音乐播放器App成为许多开发者的新选择。本次我们要实现的是音乐播放器中至关重要的&q…

作者头像 李华
网站建设 2026/10/28 7:11:53

4位OTP单片机语音智能垃圾桶实战项目

简介:本资源是一套基于STM32单片机的智能垃圾桶完整嵌入式开发工程,面向电子类专业学生、嵌入式初学者及课程设计实践者,聚焦垃圾分类场景下的传感器融合、语音交互与低功耗控制等核心问题。压缩包共226个文件,含41个.h头文件与37…

作者头像 李华
网站建设 2026/10/28 7:10:52

RankEvolve:面向可复现科研的多智能体自动研究框架

1. RankEvolve 不是“又一个AI框架”,而是研究范式的结构性迁移最近刷到“Meta发布RankEvolve多智能体自动研究框架”这个标题,很多人第一反应是:哦,Meta又出新模型了?点开一看却发现没有模型权重、没有SOTA榜单、甚至…

作者头像 李华
网站建设 2026/10/28 7:10:49

RankEvolve:面向可验证科研的多智能体自动研究框架

1. 这不是又一个“智能体玩具”,而是研究范式的底层迁移RankEvolve这个名字乍听像某个新出的AI模型代号,但如果你翻过Meta AI官网那篇不到2000词的论文预印本,会发现它根本没提“模型”二字——它连一行训练代码都没放。我第一时间下载了他们…

作者头像 李华