Feat: 使用Shell模式执行命令
- 实现ExecuteCommands使用Shell模式,在同一个会话中顺序执行 - 解决H3C设备session.Run后EOF断开问题 - 增加cleanCommandOutput清理命令回显和版权信息 - display interface等待5秒,其他命令等待2秒
This commit is contained in:
+127
-8
@@ -5,6 +5,8 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
@@ -140,19 +142,136 @@ func (c *Client) ExecuteCommand(command string) (string, error) {
|
||||
return stdoutBuf.String(), nil
|
||||
}
|
||||
|
||||
// ExecuteCommands 执行多个命令
|
||||
// ExecuteCommands 执行多个命令(使用Shell模式,在同一个会话中顺序执行)
|
||||
func (c *Client) ExecuteCommands(commands []string) ([]string, error) {
|
||||
results := make([]string, 0, len(commands))
|
||||
for _, cmd := range commands {
|
||||
result, err := c.ExecuteCommand(cmd)
|
||||
if err != nil {
|
||||
return results, fmt.Errorf("failed to execute command '%s': %w", cmd, err)
|
||||
}
|
||||
results = append(results, result)
|
||||
if c.client == nil {
|
||||
return nil, fmt.Errorf("not connected")
|
||||
}
|
||||
|
||||
// 创建一个shell会话
|
||||
session, err := c.client.NewSession()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create session: %w", err)
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
// 请求PTY
|
||||
modes := ssh.TerminalModes{
|
||||
ssh.ECHO: 0, // 禁用回显
|
||||
ssh.TTY_OP_ISPEED: 14400, // 输入速度
|
||||
ssh.TTY_OP_OSPEED: 14400, // 输出速度
|
||||
}
|
||||
if err := session.RequestPty("dumb", 200, 1000, modes); err != nil {
|
||||
return nil, fmt.Errorf("failed to request pty: %w", err)
|
||||
}
|
||||
|
||||
// 获取stdin管道
|
||||
stdin, err := session.StdinPipe()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get stdin pipe: %w", err)
|
||||
}
|
||||
|
||||
// 捕获输出
|
||||
var stdoutBuf bytes.Buffer
|
||||
var stderrBuf bytes.Buffer
|
||||
session.Stdout = &stdoutBuf
|
||||
session.Stderr = &stderrBuf
|
||||
|
||||
// 启动shell
|
||||
if err := session.Shell(); err != nil {
|
||||
return nil, fmt.Errorf("failed to start shell: %w", err)
|
||||
}
|
||||
|
||||
// 执行命令并收集输出
|
||||
results := make([]string, 0, len(commands))
|
||||
for i, cmd := range commands {
|
||||
// 等待一段时间防止设备速率限制
|
||||
if i > 0 {
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
|
||||
// 发送命令(添加换行符)
|
||||
if _, err := stdin.Write([]byte(cmd + "\n")); err != nil {
|
||||
return results, fmt.Errorf("failed to send command '%s': %w", cmd, err)
|
||||
}
|
||||
|
||||
// 等待命令执行完成(不同命令需要不同等待时间)
|
||||
sleepTime := 1 * time.Second
|
||||
if cmd == "display interface" {
|
||||
sleepTime = 5 * time.Second // 大输出命令需要更多时间
|
||||
}
|
||||
time.Sleep(sleepTime)
|
||||
|
||||
// 获取当前输出并清理
|
||||
rawOutput := stdoutBuf.String()
|
||||
cleanOutput := cleanCommandOutput(rawOutput, cmd)
|
||||
results = append(results, cleanOutput)
|
||||
|
||||
// 清空缓冲区,为下一个命令做准备(通过位置标记)
|
||||
stdoutBuf.Reset()
|
||||
stderrBuf.Reset()
|
||||
}
|
||||
|
||||
// 退出shell
|
||||
stdin.Write([]byte("exit\n"))
|
||||
session.Wait()
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// cleanCommandOutput 清理命令输出,移除命令回显、分页提示和提示符
|
||||
func cleanCommandOutput(output, command string) string {
|
||||
// 清理\r\n为\n
|
||||
output = strings.ReplaceAll(output, "\r\n", "\n")
|
||||
|
||||
lines := strings.Split(output, "\n")
|
||||
var cleanLines []string
|
||||
skipCommandEcho := true // 跳过命令本身的回显
|
||||
|
||||
for _, line := range lines {
|
||||
trimmedLine := strings.TrimSpace(line)
|
||||
|
||||
// 跳过空行(如果是开头)
|
||||
if trimmedLine == "" && len(cleanLines) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// 跳过命令回显(第一次出现)
|
||||
if skipCommandEcho && trimmedLine == strings.TrimSpace(command) {
|
||||
skipCommandEcho = false
|
||||
continue
|
||||
}
|
||||
|
||||
// 跳过分页提示
|
||||
if strings.Contains(trimmedLine, "---- More ----") {
|
||||
continue
|
||||
}
|
||||
|
||||
// 跳过提示符行(如 <hostname> 或 [hostname])
|
||||
if regexp.MustCompile(`^[<\[]\S+[>\]]$`).MatchString(trimmedLine) {
|
||||
continue
|
||||
}
|
||||
|
||||
// 跳过版权信息(开头)
|
||||
if strings.HasPrefix(trimmedLine, "*********") {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(trimmedLine, "* Copyright") {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(trimmedLine, "* Without") {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(trimmedLine, "* no decompiling") {
|
||||
continue
|
||||
}
|
||||
|
||||
cleanLines = append(cleanLines, trimmedLine)
|
||||
}
|
||||
|
||||
return strings.Join(cleanLines, "\n")
|
||||
}
|
||||
|
||||
// CheckSSH 检查主机是否开启SSH
|
||||
func CheckSSH(host string, port int, timeout time.Duration) bool {
|
||||
if port == 0 {
|
||||
|
||||
Reference in New Issue
Block a user