go标准库不支持ssh,必须使用golang.org/x/crypto/ssh官方第三方库;核心流程为ssh.dial→*ssh.client→client.newsession()→session.run(),需注意hostkeycallback、超时设置及session不可复用。

Go 标准库不支持 SSH,必须用第三方库;golang.org/x/crypto/ssh 是官方维护、最轻量且足够稳定的选择,别直接上 github.com/bramvdbogaerde/go-scp 或封装过重的 SDK。
用 golang.org/x/crypto/ssh 建立连接并执行单条命令
核心是创建 ssh.ClientConfig,再用 ssh.Dial 拿到 *ssh.Client,最后调用 client.NewSession() + session.Run()。注意:不能复用未关闭的 *ssh.Session,每次执行都应新建 Session。
常见错误包括:
- 密码认证时没设置
ssh.Password而只传了空字符串(会静默失败) - 忽略
config.HostKeyCallback—— 生产环境必须校验 host key,开发可临时用ssh.InsecureIgnoreHostKey(),但得清楚风险 - 没设超时,导致
ssh.Dial卡死在 DNS 解析或防火墙拦截上
示例关键片段:
config := &ssh.ClientConfig{
User: "ubuntu",
Auth: []ssh.AuthMethod{ssh.Password("mypass")},
HostKeyCallback: ssh.InsecureIgnoreHostKey(), // 仅测试
Timeout: 5 * time.Second,
}
client, err := ssh.Dial("tcp", "192.168.1.100:22", config)
if err != nil {
return err
}
defer client.Close()
<p>session, err := client.NewSession()
if err != nil {
return err
}
defer session.Close()</p><p>output, err := session.CombinedOutput("ls -l /tmp")
if err != nil {
return fmt.Errorf("cmd failed: %v, output: %s", err, output)
}
fmt.Println(string(output))
</p>
需要获取 stdout/stderr 分离输出?用 session.StdoutPipe 和 session.StderrPipe
当命令输出大、或需实时解析(比如 tail 日志)、或要区分错误流时,session.CombinedOutput 就不够用了。此时必须显式启动 goroutine 读取管道,否则 session.Run 会阻塞。
要点:
-
session.Start()启动命令后,才能调用Pipe方法 - 必须在
session.Wait()前完成所有读操作,否则可能丢数据或死锁 - 如果只关心 stdout,stderr 仍需读取(哪怕丢弃),否则某些远程 shell 会 hang 住
简写模式(非生产就绪,但够 debug):
session, _ := client.NewSession()
defer session.Close()
<p>var stdoutBuf, stderrBuf bytes.Buffer
session.Stdout = &stdoutBuf
session.Stderr = &stderrBuf</p><p>if err := session.Run("df -h"); err != nil {
log.Printf("run err: %v", err)
}
fmt.Println("out:", stdoutBuf.String())
fmt.Println("err:", stderrBuf.String())
</p>
执行多条命令或带环境变量?用 session.Run 包裹 sh -c
session.Run 默认只跑一个命令,不支持管道、分号、变量展开。想执行 cd /data && ./backup.sh 或 PATH=/usr/local/bin:$PATH mytool,就得靠 shell 解释器。
安全提醒:
- 不要拼接用户输入进
sh -c字符串,否则有 shell 注入风险 - 避免用
bash -c—— 远程系统不一定装 bash,sh更通用 - 环境变量建议用
session.Setenv()设置,比写进命令字符串更干净
正确写法示例:
session.Setenv("LANG", "en_US.UTF-8")
err := session.Run(`sh -c 'cd /opt/app && git pull && make restart'`)
SSH 连接本身无“登录态维持”概念,每次 ssh.Dial 都是全新 TCP 连接;如果高频调用,记得复用 *ssh.Client(不是 Session),但要注意并发安全 —— *ssh.Client 是线程安全的,Session 不是。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











