如何用Golang CLI编写脚本批量远程SSH执行命令?
Golang CLI批量SSH执行命令脚本
下面是一个完整的实现,支持从指定文件读取ip username:password格式的登录信息,批量登录远程服务器并执行指定Shell命令,同时支持并发处理提升效率。
前置依赖
首先需要安装SSH相关的依赖包:
go get golang.org/x/crypto/ssh
完整代码
package main import ( "bufio" "flag" "fmt" "log" "os" "strings" "sync" "time" "golang.org/x/crypto/ssh" ) func main() { // 解析命令行参数 cmdFlag := flag.String("cmd", "", "需要执行的Shell命令(必填)") fileFlag := flag.String("file", "logins.txt", "包含SSH登录信息的文件路径,默认logins.txt") concurrencyFlag := flag.Int("concurrency", 10, "并发执行的goroutine数量") flag.Parse() if *cmdFlag == "" { log.Fatal("必须通过 -cmd 指定要执行的Shell命令") } // 读取登录文件 file, err := os.Open(*fileFlag) if err != nil { log.Fatalf("打开文件失败: %v", err) } defer file.Close() var logins []string scanner := bufio.NewScanner(file) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if line == "" { continue } logins = append(logins, line) } if err := scanner.Err(); err != nil { log.Fatalf("读取文件失败: %v", err) } // 并发执行任务 sem := make(chan struct{}, *concurrencyFlag) var wg sync.WaitGroup for _, login := range logins { wg.Add(1) sem <- struct{}{} go func(loginStr string) { defer wg.Done() defer func() { <-sem }() // 解析登录信息:分割IP和用户名密码 parts := strings.SplitN(loginStr, " ", 2) if len(parts) != 2 { log.Printf("无效的登录格式: %s", loginStr) return } ip := parts[0] userPass := parts[1] // 分割用户名和密码 upParts := strings.SplitN(userPass, ":", 2) if len(upParts) != 2 { log.Printf("无效的用户名密码格式: %s", userPass) return } username := upParts[0] password := upParts[1] // 配置SSH客户端 config := &ssh.ClientConfig{ User: username, Auth: []ssh.AuthMethod{ ssh.Password(password), }, HostKeyCallback: ssh.InsecureIgnoreHostKey(), // 生产环境请替换为安全的HostKey验证 Timeout: 5 * time.Second, } // 建立SSH连接 client, err := ssh.Dial("tcp", fmt.Sprintf("%s:22", ip), config) if err != nil { log.Printf("连接服务器 %s 失败: %v", ip, err) return } defer client.Close() // 创建会话并执行命令 session, err := client.NewSession() if err != nil { log.Printf("服务器 %s 创建会话失败: %v", ip, err) return } defer session.Close() // 捕获命令输出 output, err := session.CombinedOutput(*cmdFlag) if err != nil { log.Printf("服务器 %s 执行命令失败: %v\n输出: %s", ip, err, string(output)) return } fmt.Printf("=== 服务器 %s 执行结果 ===\n%s\n", ip, string(output)) }(login) } wg.Wait() log.Println("所有任务执行完成") }
代码说明
命令行参数解析:
-cmd:必填参数,指定要在远程服务器执行的Shell命令-file:可选参数,指定登录信息文件路径,默认使用当前目录的logins.txt-concurrency:可选参数,指定并发执行的goroutine数量,默认10(可根据服务器性能调整)
登录信息解析:
- 逐行读取文件内容,跳过空行
- 将每行按空格分割为IP和
username:password部分,再按冒号分割出用户名和密码
SSH连接与命令执行:
- 创建SSH客户端配置,设置密码认证、连接超时
- 建立TCP连接到远程服务器的22端口
- 创建SSH会话,通过
CombinedOutput执行命令并同时捕获标准输出和错误输出
并发控制:
- 使用信号量(
sem通道)限制并发数,避免同时发起过多连接导致资源耗尽 - 使用
sync.WaitGroup等待所有goroutine执行完成
- 使用信号量(
使用示例
- 准备
logins.txt文件,每行格式如下:
192.168.1.10 root:123456 192.168.1.11 admin:abcdef ...
- 编译并运行脚本:
go build -o ssh-exec ./ssh-exec -cmd "uptime && df -h" -file ./logins.txt -concurrency 15
注意事项
- 安全问题:
ssh.InsecureIgnoreHostKey()会跳过主机密钥验证,生产环境中请替换为ssh.FixedHostKey或使用已知主机文件验证,避免中间人攻击;同时建议使用SSH密钥认证替代密码明文存储 - 并发调整:并发数不要设置过高,否则可能触发远程服务器的连接限制
- 错误处理:脚本会打印每个服务器的连接/执行错误,方便排查问题
- 超时设置:已设置5秒连接超时,可根据网络情况调整
Timeout值
内容的提问来源于stack exchange,提问作者Jamal Gains
相关产品推荐
相关产品推荐

