首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在一次访问中发送多个命令,但单独保存输出

如何在一次访问中发送多个命令,但单独保存输出
EN

Stack Overflow用户
提问于 2020-06-22 21:11:03
回答 3查看 1K关注 0票数 1

我的代码应该是SSH到远程主机(比如说路由器),并在远程主机上运行多个命令并返回输出。

所附代码经过简化,分为三部分:

  • Main函数:读取命令列表,然后使用ExecCommands函数拨号/ SSH到远程主机执行commands.
  • ExecCommands函数,获取用于SSH的远程主机IP、命令列表和SSH ClientConfig。然后它拨号到IP,然后一个接一个地运行命令。最后,只返回一个string
  • InsecureClientConfig函数中的所有命令的输出,该函数实际上除了创建一个用于ExecCommands函数

的SSH ClientConfig之外,没有什么作用。

当我只想应用一些命令或配置并保存整个结果时,这个程序工作得很好。我的意思是,ExecCommands接受大量命令,将它们全部推送到远程主机,并将应用命令的全部输出以一个字符串作为输出返回(或保存)。

问题:

  • I不能单独处理每个命令的输出。例如,假设我应用了CMD1、CMD2、CMD3、…使用ExecCommands函数访问远程主机#1。由于它以一个字符串的形式返回了整个输出,因此很难找到哪个输出属于CMD

目标:

  • 修改或重新设计ExecCommands函数,以便为其应用的每个命令提供单独的输出。这意味着如果对于remote-host#1应用10个命令,我应该有10个单独的字符串作为输出。

Conditions/Restrictions:

  • I可以为命令创建任何额外的会话,并且必须在我创建的第一个SSH会话中应用所有命令,即不能创建多个会话并在SSH包

H 141中使用RunShellOutputStart函数,不允许重新身份验证。例如,我只有一个可以用于所有remote-hosts.

  • Remote主机的一次性密码,不支持类似于Linux
  • 的命令--远程主机不支持任何类型的APIs

点:

  • 的主要关注点是函数ExecCommands。我把整个代码的一个简化版本给出了一个想法,
  • ,我使用stdout, err := session.StdoutPipe()运行多个命令,这意味着-as管道-它的阅读器只有在工作完成后才能被读取。
  • ,一种选择是在ExecCommands函数中的for循环中使用Session.StdoutSession.Stdin。尝试过,但没有成功。

代码:

代码语言:javascript
运行
复制
package main

import (
    "errors"
    "fmt"
    "io/ioutil"
    "log"
    "time"

    "golang.org/x/crypto/ssh"
)

func main() {

    // List of the commands should be sent to the devices
    listCMDs := []string{
        "set cli op-command-xml-output on",
        "test routing fib-lookup virtual-router default ip 1.1.1.1",
        "test routing fib-lookup virtual-router default ip 2.2.2.2",
        "show interface ethernet1/1",
        "show interface ethernet1/2",
        "test security-policy-match protocol 6 source 1.1.1.1 destination 2.2.2.2 destination-port 443 from ZONE1 to ZONE2",
        "test security-policy-match protocol 6 source 10.0.0.1 destination 10.0.2.1 destination-port 443 from ZONE1 to ZONE2",
        "exit",
    }

    sshconfig := InsecureClientConfig("admin", "admin")

    s, err := ExecCommands("192.168.1.250", listCMDs, sshconfig)
    fmt.Println(s, err)
}

// ExecCommands ...
func ExecCommands(ipAddr string, commands []string, sshconfig *ssh.ClientConfig) (string, error) {

    // Gets IP, credentials and config/commands, SSH Config (Timeout, Ciphers, ...) and returns
    // output of the device as "string" and an error. If error == nil, means program was able to SSH with no issue

    // Creating outerr as Output Error.
    outerr := errors.New("nil")
    outerr = nil

    // Creating Output as String
    var outputStr string

    // Dial to the remote-host
    client, err := ssh.Dial("tcp", ipAddr+":22", sshconfig)
    if err != nil {
        log.Fatal(err)
    }
    defer client.Close()

    // Create sesssion
    session, err := client.NewSession()
    if err != nil {
        log.Fatal(err)
    }
    defer session.Close()

    // StdinPipee() returns a pipe that will be connected to the remote command's standard input when the command starts.
    // StdoutPipe() returns a pipe that will be connected to the remote command's standard output when the command starts.
    stdin, err := session.StdinPipe()
    if err != nil {
        log.Fatal(err)
    }

    stdout, err := session.StdoutPipe()
    if err != nil {
        log.Fatal(err)
    }

    // Start remote shell
    err = session.Shell()
    if err != nil {
        log.Fatal(err)
    }

    // Send the commands to the remotehost one by one.
    for _, cmd := range commands {
        _, err := stdin.Write([]byte(cmd + "\n"))
        if err != nil {
            log.Fatal(err)
        }
    }

    // Wait for session to finish
    err = session.Wait()
    if err != nil {
        log.Fatal(err)
    }

    strByte, _ := ioutil.ReadAll(stdout)
    outputStr = string(strByte)

    return outputStr, outerr
}

// InsecureClientConfig ...
func InsecureClientConfig(userStr, passStr string) *ssh.ClientConfig {

    SSHconfig := &ssh.ClientConfig{
        User:    userStr,
        Timeout: 5 * time.Second,
        Auth:    []ssh.AuthMethod{ssh.Password(passStr)},

        HostKeyCallback: ssh.InsecureIgnoreHostKey(),
        Config: ssh.Config{
            Ciphers: []string{"aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-cbc", "aes192-cbc",
                "aes256-cbc", "3des-cbc", "des-cbc"},
            KeyExchanges: []string{"diffie-hellman-group1-sha1",
                "diffie-hellman-group-exchange-sha1",
                "diffie-hellman-group14-sha1"},
        },
    }
    return SSHconfig
}
EN

Stack Overflow用户

发布于 2020-11-19 08:20:17

由于在特殊硬件上运行的命令数量有限,并且知道每个命令的输出模式,所以可以使用strings.Splitregexp来拆分输出。

如果您没有echo命令,但是知道任何具有唯一输出模式的快速响应的命令,那么您可以在下面的示例(数字2)中用echo命令替换它。

由于会话只接受,因此调用RunStartShellOutputCombinedOutput,而不希望在每个命令下启动新会话:

关键是在发送命令之前使用strings.Builder并使用sb.Reset()将其清空,并使用io.Copy将会话的stdout复制到strings.Builder (假设您执行而不是需要会话的stderr):

代码语言:javascript
运行
复制
sb := new(strings.Builder)
go io.Copy(sb, stdout)

如果您知道等待每个命令(测试)的时间是多少:

代码语言:javascript
运行
复制
sb := new(strings.Builder)
go io.Copy(sb, stdout)

commands := []string{"uname -a", "sleep 1", "pwd", "whoami", "exit"}
wait := []time.Duration{10, 1200, 20, 10, 10} // * time.Millisecond
ans := []string{}

time.Sleep(10 * time.Millisecond) // wait for the ssh greetings

// Send the commands to the remotehost one by one.
for i, cmd := range commands {
    sb.Reset()
    fmt.Println("*** command:\t", cmd)
    _, err := stdin.Write([]byte(cmd + "\n"))
    if err != nil {
        log.Fatal(err)
    }
    time.Sleep(wait[i] * time.Millisecond) // wait for the command to finish
    s := sb.String()
    fmt.Println("*** response:\t", s)
    ans = append(ans, s)
}

  1. 使用字符串分隔符和strings.Split (注意:您可以用任何快速命令替换echo,并使用已知的输出模式):

代码语言:javascript
运行
复制
sb := new(strings.Builder)
go io.Copy(sb, stdout)
commands := []string{"uname -a", "sleep 1", "pwd", "whoami"}
delim := "********--------========12345678"
for _, cmd := range commands {
    _, err = stdin.Write([]byte("echo " + delim + "\n"))
    if err != nil {
        log.Fatal(err)
    }
    _, err := stdin.Write([]byte(cmd + "\n"))
    if err != nil {
        log.Fatal(err)
    }
}
_, err = stdin.Write([]byte("exit\n"))
if err != nil {
    log.Fatal(err)
}
err = session.Wait() // Wait for session to exit
if err != nil {
    log.Fatal(err)
}
ans := strings.Split(sb.String(), delim)
ans = ans[1:] // remove ssh greetings
票数 0
EN
查看全部 3 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62523552

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档