You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Golang SSH会话与远程服务器断开时如何停止运行中的SSH脚本

问题原因

你遇到的wait: remote command exited without exit status or exit signal错误,本质是SSH会话对应的远端sshd进程异常终止(比如被SIGKILL强杀),没有给客户端返回正常的命令退出状态码。
出现脚本残留运行的核心原因:

  • SIGKILL信号是用户态进程完全无法捕获的,sshd进程收到kill -9发送的SIGKILL后会被内核直接回收,完全没有机会执行会话清理、给子进程发终止信号的逻辑
  • 默认无PTY启动的SSH命令,如果没有做进程组绑定,子进程会被init进程领养,脱离原会话的管控,就会持续在后台运行
实现方案

方案1:依赖内核PTY机制自动清理(最可靠,优先选择)

给SSH会话分配伪终端(PTY)后,远端启动的所有命令都会归属到PTY对应的前台进程组。当SSH会话断开、PTY主端关闭时,内核会自动给该PTY关联的所有进程发送SIGHUP信号,默认行为是终止进程组内所有进程,完全不依赖sshd的用户态清理逻辑,哪怕sshd被SIGKILL强杀也能生效。
Go侧实现代码示例:

import "golang.org/x/crypto/ssh"

// 假设client是已经建立好的ssh.Client实例
session, err := client.NewSession()
if err != nil {
    // 处理错误逻辑
}
defer session.Close()

// 配置终端参数,申请PTY,必须在Start命令前执行
terminalModes := ssh.TerminalModes{
    ssh.ECHO:          0, // 关闭终端回显
    ssh.TTY_OP_ISPEED: 14400,
    ssh.TTY_OP_OSPEED: 14400,
}
if err := session.RequestPty("xterm-256color", 80, 40, terminalModes); err != nil {
    // 处理错误逻辑
}

// 关联会话输入输出后启动目标脚本
session.Stdout = os.Stdout
session.Stderr = os.Stderr
session.Stdin = os.Stdin
if err := session.Start("/path/to/your/script.sh"); err != nil {
    // 处理错误逻辑
}
// 阻塞等待命令执行完成
if err := session.Wait(); err != nil {
    // 此处可捕获包括会话异常断开在内的所有执行错误
}

方案2:兜底主动清理(适配无PTY场景)

如果业务场景不允许分配PTY,可以通过PID标记+异常后主动杀进程的方式兜底:

  • 启动远端脚本时,先由Go侧生成一个全局唯一的任务ID,将脚本主进程PID写入带该ID的临时文件,同时将脚本放到独立进程组运行,启动命令示例:
    UNIQUE_ID=<Go侧生成的随机唯一任务ID>
    echo $$ > /tmp/ssh_task_${UNIQUE_ID}.pid && exec setsid /path/to/your/script.sh
    
  • 在Go侧捕获到session.Wait()返回的异常(包括你遇到的无退出状态的错误)时,重新建立一个短SSH连接到目标服务器,执行清理命令,递归终止整个进程组的残留进程:
    UNIQUE_ID=<对应会话的唯一任务ID>
    PID=$(cat /tmp/ssh_task_${UNIQUE_ID}.pid)
    PGID=$(ps -o pgid= ${PID} | tr -d ' ')
    kill -TERM -${PGID}
    rm -f /tmp/ssh_task_${UNIQUE_ID}.pid
    
    注意kill的PID参数前加负号,代表杀死整个进程组,避免漏杀脚本拉起的子进程。
注意事项

不要依赖sshd的用户态清理逻辑处理SIGKILL场景:SIGKILL信号无法被用户态进程捕获,sshd被kill -9强杀时,所有注册的清理回调都不会执行,只有内核级的PTY SIGHUP机制、主动连接清理逻辑能保证残留进程被终止。
如果你的脚本内部显式设置了忽略SIGHUP信号,需要在脚本开头添加trap "exit" SIGHUP SIGTERM,捕获终止信号后主动退出。

内容的提问来源于stack exchange,提问作者yuanmh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 01:06:23