这段代码中的executeSSHCommand
函数会在启动一个协程后立即返回”Command executed successfully”,而不是等待远程命令执行完成并返回实际输出。因此,最终的API响应总是{"output": "Command executed successfully"}
,无论远程命令是否成功执行。
要解决这个问题,可以使用一个带有缓冲区的通道来获取实际输出,并在协程中从标准输出流中读取数据并将其发送到通道中。然后,在主函数中等待通道关闭并读取其中的所有数据作为API响应。下面是修正后的代码:
package main
import (
"bufio"
"fmt"
"golang.org/x/crypto/ssh"
"io/ioutil"
"os"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
// 定义POST路由 /execute
r.POST("/execute", func(c *gin.Context) {
command := c.PostForm("command") // 从请求中获取命令参数
if len(command) == 0 {
c.JSON(400, gin.H{"error": "Command parameter is required"})
return
}
output, err := executeSSHCommand(command) // 执行SSH命令
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{"output": output})
})
r.Run(":8080")
}
func executeSSHCommand(cmd string) (string, error) {
remoteAddr := "<remote-server-address>"
sshPort := 22
auth := []ssh.AuthMethod{ssh.Password("<your-password>")}
config := &ssh.ClientConfig{
User: "<your-username>",
Auth: auth,
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
conn, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", remoteAddr, sshPort), config)
if err != nil {
return "", err
}
defer conn.Close()
session, err := conn.NewSession()
if err != nil {
return "", err
}
defer session.Close()
stdout, err := session.StdoutPipe()
if err != nil {
return "", err
}
stdin, err := session.StdinPipe()
if err != nil {
return "", err
}
scanner := bufio.NewScanner(stdout)
// 创建一个带缓冲区的通道来获取实际输出
outputChan := make(chan string, 100)
// 在协程中从标准输出流中读取数据并将其发送到通道中
go func() {
for scanner.Scan() {
outputChan <- scanner.Text()
}
if scanner.Err() != nil {
fmt.Println(scanner.Err())
close(outputChan)
return
}
close(outputChan)
}()
_, _ = stdin.Write([]byte(cmd + "\n"))
var output string
// 等待通道关闭并读取其中的所有数据作为API响应。
for line := range outputChan {
output += line + "\n"
}
// 等待命令执行完成,然后返回错误信息(如果有)
if waitErr := session.Wait(); waitErr != nil {
return output, waitErr
}
// 如果没有错误,则返回实际输出
return output, nil
}
在上面的代码中,我们创建了一个带缓冲区的通道outputChan
来获取实际输出。在协程中,我们从标准输出流中读取数据并将其发送到通道中。在主函数中,我们等待通道关闭并读取其中的所有数据作为API响应。最后,在等待命令执行完成之后,我们检查是否有任何错误,并根据需要返回它们。
注意:该修复代码没有处理超时、连接失败和其他异常情况,需要根据具体情况进行修改。