Feat: 支持H3C/华为设备分页禁用
- 使用Shell模式替代Run模式执行命令 - 自动发送screen-length disable禁用分页 - 清理输出中的命令回显和分页提示 - 解决display interface输出为空的问题
这个提交包含在:
+82
-4
@@ -5,7 +5,9 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
@@ -143,16 +145,92 @@ func (c *Client) ExecuteCommand(command string) (string, error) {
|
|||||||
}
|
}
|
||||||
defer session.Close()
|
defer session.Close()
|
||||||
|
|
||||||
|
// H3C/华为设备需要先禁用分页
|
||||||
|
// 使用 Shell 模式而不是 Run 模式
|
||||||
|
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 "", fmt.Errorf("failed to request pty: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动 shell
|
||||||
|
shell, err := session.StdinPipe()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to get stdin pipe: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
var stdoutBuf bytes.Buffer
|
var stdoutBuf bytes.Buffer
|
||||||
session.Stdout = &stdoutBuf
|
session.Stdout = &stdoutBuf
|
||||||
session.Stderr = &stdoutBuf
|
session.Stderr = &stdoutBuf
|
||||||
|
|
||||||
err = session.Run(command)
|
if err := session.Shell(); err != nil {
|
||||||
if err != nil {
|
return "", fmt.Errorf("failed to start shell: %w", err)
|
||||||
return stdoutBuf.String(), fmt.Errorf("command execution failed: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return stdoutBuf.String(), nil
|
// 先发送禁用分页命令(H3C/华为)
|
||||||
|
if _, err := shell.Write([]byte("screen-length disable\n")); err != nil {
|
||||||
|
return "", fmt.Errorf("failed to send screen-length disable: %w", err)
|
||||||
|
}
|
||||||
|
time.Sleep(500 * time.Millisecond) // 等待命令执行
|
||||||
|
|
||||||
|
// 发送实际命令
|
||||||
|
if _, err := shell.Write([]byte(command + "\n")); err != nil {
|
||||||
|
return "", fmt.Errorf("failed to send command: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待命令执行完成
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
if _, err := shell.Write([]byte("exit\n")); err != nil {
|
||||||
|
return "", fmt.Errorf("failed to send exit: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待会话结束
|
||||||
|
session.Wait()
|
||||||
|
|
||||||
|
output := stdoutBuf.String()
|
||||||
|
|
||||||
|
// 清理输出:移除命令回显和分页提示
|
||||||
|
lines := strings.Split(output, "\n")
|
||||||
|
var cleanLines []string
|
||||||
|
skipNext := false
|
||||||
|
for _, line := range lines {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
|
||||||
|
// 跳过空行
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跳过分页提示
|
||||||
|
if strings.Contains(line, "---- More ----") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跳过命令本身的回显
|
||||||
|
if line == command || line == "screen-length disable" {
|
||||||
|
skipNext = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跳过提示符行(如 <hostname> 或 [hostname])
|
||||||
|
if regexp.MustCompile(`^[<\[]\S+[>\]]$`).MatchString(line) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是 "screen-length disable" 后的第一行(通常是提示符),跳过
|
||||||
|
if skipNext {
|
||||||
|
skipNext = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanLines = append(cleanLines, line)
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(cleanLines, "\n"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecuteCommands 执行多个命令
|
// ExecuteCommands 执行多个命令
|
||||||
|
|||||||
在新工单中引用
屏蔽一个用户