# sshpsgo **Repository Path**: graviton/sshpsgo ## Basic Information - **Project Name**: sshpsgo - **Description**: sshpass in golang - **Primary Language**: Unknown - **License**: AGPL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-06-18 - **Last Updated**: 2026-09-20 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # sshpsgo 用 Go 实现 `sshpass-1.10` 的核心行为. 这个命令会为子进程创建一个伪终端, 监听该终端里的 SSH 密码提示, 并在匹配到提示后写入一次配置好的密码. Unix 上使用传统 PTY 和控制终端; Windows 上使用 ConPTY 运行 OpenSSH, 并在密码发送后进入交互输入输出转发. 批处理调用可在目标命令前加 `--plain-output`. 该模式仍通过 ConPTY 响应密码提示, 但会把 ConPTY 光标定位还原为普通换行和空格, 并禁用登录后的 stdin 转发, 避免默认 80 列终端宽度破坏长路径或结构化输出. 默认交互行为不变. ## 构建 构建脚本是 `build.go` —— 只用标准库的单文件, 不需要先编译它, 三个平台同一条命令: ```sh go run build.go # 本机平台 → build/sshpass go run build.go -t linux/amd64 # 交叉编译 (可重复 -t) go run build.go -all # 六个发布目标 (与 CI 一致) go run build.go -vet -test # 构建前过 go vet / go test go run build.go -n # 预演, 只打印要跑什么 ``` 产物为静态链接、去掉符号表与本机路径 (`CGO_ENABLED=0`, `-trimpath`, `-ldflags="-s -w"`); `-debug` 保留符号表给 delve 用。默认顺带跑一次 `go fmt ./...`, `-no-fmt` 关掉。产物命名: `linux/amd64` → `build/sshpass` (deploy.go 按这个名字读), `windows/amd64` → `build/sshpass.exe`, 其余 → `build/sshpass--[.exe]`。 旧的 `build.sh` / `build.nu` 已挪到 `backup/` 备用, 不要复活。 也可以直接使用 Go 构建: ```sh go build -o build/sshpass ./cmd/sshpass ``` Windows: ```powershell go build -o build\sshpass.exe .\cmd\sshpass ``` ## 部署到本机 ```sh go run deploy.go # 装到 /data/cmbin (主) 与 ~/bin (次) go run deploy.go -n # 预演, 不写盘 go run deploy.go -t /tmp/try -mkdir # 换目标 (可重复给) go run deploy.go -binary build/sshpass-linux-arm64 # 装别的那份产物 ``` `/data/cmbin/sshpass` 是各 docker 共享的那一份, 装上去就顶掉现有的同名工具。 它比 `cp` 多做三件事: 先验产物是不是 Linux x86-64 ELF (`/data/cmbin` 整棵树会被 `chmdply push --task cmbin` 分发到十几台机器, 装错平台会跟着扩散出去); 写入走临时 文件 + 原子改名 (目标正被执行时 `cp` 会 `ETXTBSY`); 内容相同则不动。 ## 用法 ```text sshpass [-f|-d|-p|-e[env_var]] [-P prompt] [-v] [--plain-output] [-hV] command parameters ``` 支持的密码来源: - `-p password`: 通过命令行参数提供密码. - `-f filename`: 使用文件第一行作为密码. - `-d number`: 从继承的文件描述符读取密码. - `-e[env_var]`: 从环境变量读取密码; 不指定变量名时默认使用 `SSHPASS`. - 不指定密码来源选项时: 从 stdin 读取密码. 其他支持的选项: - `-P prompt`: 覆盖默认密码提示匹配字符串. - `-v`: 向 stderr 输出调试信息 (可重复, 越多越细). - `--plain-output`: Windows 批处理模式 —— 仍用 ConPTY 响应密码提示, 但把屏幕更新 还原成普通换行与空格并禁用登录后的 stdin 转发 (详见开头一段与"Windows 输出排空 与限制"). 非 Windows 平台上这个选项没有作用. - `-h`: 显示帮助. - `-V`: 显示版本信息. 密码来源选项最多只能指定一个. 默认密码提示匹配逻辑遵循 `sshpass-1.10` 源码行为, 搜索字符串为 `assword`. ## 示例 使用密码 `114514` 连接 `10.0.255.5:13031`, 并在远端执行 `echo SSH_OK`: ```sh ./build/sshpass -p 114514 ssh -p 13031 -o ConnectTimeout=10 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o PreferredAuthentications=password -o PubkeyAuthentication=no 10.0.255.5 'echo SSH_OK' ``` Windows: ```powershell .\build\sshpass.exe -p 114514 ssh -p11061 -o ConnectTimeout=10 -o StrictHostKeyChecking=no -o UserKnownHostsFile=NUL -o PreferredAuthentications=password -o PubkeyAuthentication=no app@10.0.255.12 echo SSH_OK ``` Windows 交互 shell: ```powershell .\build\sshpass.exe -p 114514 ssh -p11061 -o ConnectTimeout=10 -o StrictHostKeyChecking=no -o UserKnownHostsFile=NUL -o PreferredAuthentications=password -o PubkeyAuthentication=no app@10.0.255.12 ``` ## 返回码 该实现保留原版主要返回码: - `0`: 成功. - `1`: 无效命令行参数. - `2`: 密码来源参数冲突. - `3`: 通用运行时错误. - `4`: 解析错误. - `5`: 密码无效或错误. - `6`: 未知主机密钥提示. - `7`: 主机密钥变更提示. 如果子进程正常退出, 并且没有检测到 sshpass 自身的错误, 则返回子进程的退出码. ## 注意事项 `-p` 会在程序有机会降低暴露前, 把密码暴露在进程参数里. 脚本中建议优先使用 `-d`, `-f` 或 `-e`. Windows 原生支持依赖 Windows ConPTY, 目标命令应是 Windows 控制台程序 或能在 ConPTY 中正常运行的程序. 交互模式下程序会在登录前检测密码提示, 发送密码后把本地标准输入转发给远端 shell, 并让 ConPTY 输出透传到当前终端. 如果把输出重定向到文件或管道, 可能会看到远端程序产生的 VT 控制序列. ### Windows 输出排空与限制 当前 Windows 实现使用 `github.com/UserExistsError/conpty v0.1.4`. 该版本的 `Wait()` 只表示子进程已经退出, 不表示 ConPTY 缓冲区中的输出已经被读取完成; 同时, 子进程退出后输出 reader 不会主动收到 EOF. 如果在 `Wait()` 返回后立即调用 `Close()`, 尾部输出可能被截断, 甚至截在 UTF-8 字符中间. 为避免截断, 当前实现会在子进程退出后继续转发输出, 并在每次收到新输出时重置 500 ms 静默计时器. 输出持续静默 500 ms 后才关闭 ConPTY, 然后等待输出 monitor 完成最后一次 `Flush()` 再返回. 输出活动通知通过容量为 1 的非阻塞 channel 合并, 不会让输出读取因主循环来不及消费通知而阻塞. 这个方案的限制是每次 Windows 命令结束时通常会增加约 500 ms 延迟. 对 SSH 等长任务 影响较小, 但大量连续执行的短命令会明显受到影响. 静默窗口也仍是经验值, 不是由 EOF 驱动的确定性完成信号. `conpty v0.1.4` 的 `Close()` 会同时关闭伪控制台和本地输出读句柄, 因此目前无法先关闭 输出产生端、再保留 reader 读到 EOF. 如果未来 ConPTY 库提供分离的关闭接口, 应改为: 1. 子进程退出后单独关闭伪控制台或输出产生端. 2. 保持本地输出读句柄有效, 继续读取直到确定性 EOF. 3. 等待输出 monitor 完成最后一次 `Flush()`. 4. 最后关闭其余句柄并返回子进程退出码. 完成上述迁移后可以移除固定的 500 ms 静默窗口及其短命令延迟. 修改 Windows ConPTY 生命周期或输出转发代码后, 应至少运行以下回归测试. 该测试会让 ConPTY 子进程快速写入大量中文并立即退出, 验证最终 UTF-8 尾标记没有被截断: ```powershell go test ./cmd/sshpass -run TestRunProgramDrainsUTF8OutputAfterProcessExit -count=10 go test ./... ```