如何在一个会话中发送多个命令但分别保存输出

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

随附的代码经过简化,分为三个部分:

  • Main功能:读取命令列表,然后使用ExecCommands功能 dials/ssh 到远程主机执行命令。

  • ExecCommands函数获取远程主机 IP、命令列表和ClientConfig用于 SSH 的 SSH。然后它拨号到 IP 并一个接一个地运行命令。最后,仅在一个字符串中返回所有命令的输出

  • InsecureClientConfigClientConfig除了创建一个用于函数的 SSH 之外实际上并没有做太多的ExecCommands函数

当我只想应用一些命令或配置并保存整体结果时,该程序运行良好。我的意思是ExecCommands获取一堆命令,将它们全部推送到远程主机,然后在一个字符串中返回(或保存)应用命令的整个输出作为输出。

问题:

  • 我无法单独处理每个命令的输出。例如,假设我通过使用 ExecCommands 函数将 CMD1、CMD2、CMD3……应用于远程主机#1。由于它在一个字符串中将整个输出返回给我,因此很难找到哪个输出属于哪个 CMD

目标:

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

条件/限制:

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

  • 不允许重新认证。例如,我只有一个一次性密码,可用于所有远程主机。

  • 远程主机不支持类似于 Linux 中的“回显”命令

  • 远程主机不支持任何类型的 API

要点:

  • 主要重点是功能ExecCommands。我放了整个代码的简化版本来给出一个想法

  • stdout, err := session.StdoutPipe()用来运行多个命令,这意味着 -as pipe - 只有在工作完成后才能读取 Reader。

  • 一个选项是在函数的 for 循环中使用Session.Stdoutand 。试过但没有成功。Session.StdinExecCommands


鸿蒙传说
浏览 153回答 3
3回答

一只萌萌小番薯

这可以正常工作:package mainimport (&nbsp; &nbsp; "bufio"&nbsp; &nbsp; "errors"&nbsp; &nbsp; "fmt"&nbsp; &nbsp; "log"&nbsp; &nbsp; "time"&nbsp; &nbsp; "golang.org/x/crypto/ssh")func main() {&nbsp; &nbsp; // List of the commands should be sent to the devices&nbsp; &nbsp; listCMDs := []string{&nbsp; &nbsp; &nbsp; &nbsp; "set cli op-command-xml-output on\n",&nbsp; &nbsp; &nbsp; &nbsp; "test routing fib-lookup virtual-router default ip 1.1.1.1\n",&nbsp; &nbsp; &nbsp; &nbsp; "test routing fib-lookup virtual-router default ip 2.2.2.2\n",&nbsp; &nbsp; &nbsp; &nbsp; "show interface ethernet1/1\n",&nbsp; &nbsp; &nbsp; &nbsp; "show interface ethernet1/2\n",&nbsp; &nbsp; &nbsp; &nbsp; "test security-policy-match protocol 6 source 1.1.1.1 destination 2.2.2.2 destination-port 443 from ZONE1 to ZONE2\n",&nbsp; &nbsp; &nbsp; &nbsp; "test security-policy-match protocol 6 source 10.0.0.1 destination 10.0.2.1 destination-port 443 from ZONE1 to ZONE2\n",&nbsp; &nbsp; &nbsp; &nbsp; "exit",&nbsp; &nbsp; }&nbsp; &nbsp; sshconfig := InsecureClientConfig("admin", "Ghazanfar1!")&nbsp; &nbsp; s, _ := ExecCommands("192.168.1.249", listCMDs, sshconfig)&nbsp; &nbsp; for _, item := range s {&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(item)&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("-------------------------------")&nbsp; &nbsp; }}// ExecCommands ...func ExecCommands(ipAddr string, commands []string, sshconfig *ssh.ClientConfig) ([]string, error) {&nbsp; &nbsp; // Gets IP, credentials and config/commands, SSH Config (Timeout, Ciphers, ...) and returns&nbsp; &nbsp; // output of the device as "string" and an error. If error == nil, means program was able to SSH with no issue&nbsp; &nbsp; // Creating outerr as Output Error.&nbsp; &nbsp; outerr := errors.New("nil")&nbsp; &nbsp; outerr = nil&nbsp; &nbsp; // Creating Output as String&nbsp; &nbsp; var outputStr []string&nbsp; &nbsp; var strTmp string&nbsp; &nbsp; // Dial to the remote-host&nbsp; &nbsp; client, err := ssh.Dial("tcp", ipAddr+":22", sshconfig)&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; log.Fatal(err)&nbsp; &nbsp; }&nbsp; &nbsp; defer client.Close()&nbsp; &nbsp; // Create sesssion&nbsp; &nbsp; session, err := client.NewSession()&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; log.Fatal(err)&nbsp; &nbsp; }&nbsp; &nbsp; defer session.Close()&nbsp; &nbsp; // StdinPipee() returns a pipe that will be connected to the remote command's standard input when the command starts.&nbsp; &nbsp; // StdoutPipe() returns a pipe that will be connected to the remote command's standard output when the command starts.&nbsp; &nbsp; stdin, err := session.StdinPipe()&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; log.Fatal(err)&nbsp; &nbsp; }&nbsp; &nbsp; stdout, err := session.StdoutPipe()&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; log.Fatal(err)&nbsp; &nbsp; }&nbsp; &nbsp; // Start remote shell&nbsp; &nbsp; err = session.Shell()&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; log.Fatal(err)&nbsp; &nbsp; }&nbsp; &nbsp; stdinLines := make(chan string)&nbsp; &nbsp; go func() {&nbsp; &nbsp; &nbsp; &nbsp; scanner := bufio.NewScanner(stdout)&nbsp; &nbsp; &nbsp; &nbsp; for scanner.Scan() {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; stdinLines <- scanner.Text()&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; if err := scanner.Err(); err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; log.Printf("scanner failed: %v", err)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; close(stdinLines)&nbsp; &nbsp; }()&nbsp; &nbsp; // Send the commands to the remotehost one by one.&nbsp; &nbsp; for i, cmd := range commands {&nbsp; &nbsp; &nbsp; &nbsp; _, err := stdin.Write([]byte(cmd + "\n"))&nbsp; &nbsp; &nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; log.Fatal(err)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; if i == len(commands)-1 {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _ = stdin.Close() // send eof&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; // wait for command to complete&nbsp; &nbsp; &nbsp; &nbsp; // we'll assume the moment we've gone 1 secs w/o any output that our command is done&nbsp; &nbsp; &nbsp; &nbsp; timer := time.NewTimer(0)&nbsp; &nbsp; InputLoop:&nbsp; &nbsp; &nbsp; &nbsp; for {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; timer.Reset(time.Second)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; select {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case line, ok := <-stdinLines:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if !ok {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; log.Println("Finished processing")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break InputLoop&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; strTmp += line&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; strTmp += "\n"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case <-timer.C:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break InputLoop&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; outputStr = append(outputStr, strTmp)&nbsp; &nbsp; &nbsp; &nbsp; //log.Printf("Finished processing %v\n", cmd)&nbsp; &nbsp; &nbsp; &nbsp; strTmp = ""&nbsp; &nbsp; }&nbsp; &nbsp; // Wait for session to finish&nbsp; &nbsp; err = session.Wait()&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; log.Fatal(err)&nbsp; &nbsp; }&nbsp; &nbsp; return outputStr, outerr}// InsecureClientConfig ...func InsecureClientConfig(userStr, passStr string) *ssh.ClientConfig {&nbsp; &nbsp; SSHconfig := &ssh.ClientConfig{&nbsp; &nbsp; &nbsp; &nbsp; User:&nbsp; &nbsp; userStr,&nbsp; &nbsp; &nbsp; &nbsp; Timeout: 5 * time.Second,&nbsp; &nbsp; &nbsp; &nbsp; Auth:&nbsp; &nbsp; []ssh.AuthMethod{ssh.Password(passStr)},&nbsp; &nbsp; &nbsp; &nbsp; HostKeyCallback: ssh.InsecureIgnoreHostKey(),&nbsp; &nbsp; &nbsp; &nbsp; Config: ssh.Config{&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Ciphers: []string{"aes128-ctr", "aes192-ctr", "aes256-ctr", "aes128-cbc", "aes192-cbc",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "aes256-cbc", "3des-cbc", "des-cbc"},&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; KeyExchanges: []string{"diffie-hellman-group1-sha1",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "diffie-hellman-group-exchange-sha1",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "diffie-hellman-group14-sha1"},&nbsp; &nbsp; &nbsp; &nbsp; },&nbsp; &nbsp; }&nbsp; &nbsp; return SSHconfig}

宝慕林4294392

由于您在特殊硬件上运行的命令数量有限,并且您知道每个命令的输出模式,因此您可以使用strings.Split或regexp拆分输出。如果您没有命令,但知道任何具有独特输出模式的快速响应命令,那么您可以在以下示例中将其echo替换为命令(编号 2)。echo由于会话只接受对、、、或的一次调用,并且您不想为每个命令启动一个新会话:RunStartShellOutputCombinedOutput关键是在发送命令之前使用 astrings.Builder并将其清空,并使用将会话的标准输出同时复制 到(假设您不需要会话的标准错误):sb.Reset()io.Copystrings.Buildersb := new(strings.Builder)go io.Copy(sb, stdout)如果您知道每个命令要等待多少时间(已测试),则此方法有效: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.Millisecondans := []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 {&nbsp; &nbsp; sb.Reset()&nbsp; &nbsp; fmt.Println("*** command:\t", cmd)&nbsp; &nbsp; _, err := stdin.Write([]byte(cmd + "\n"))&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; log.Fatal(err)&nbsp; &nbsp; }&nbsp; &nbsp; time.Sleep(wait[i] * time.Millisecond) // wait for the command to finish&nbsp; &nbsp; s := sb.String()&nbsp; &nbsp; fmt.Println("*** response:\t", s)&nbsp; &nbsp; ans = append(ans, s)}使用字符串分隔符和strings.Split(注意:您可以echo用任何已知输出模式的快速命令替换):sb := new(strings.Builder)go io.Copy(sb, stdout)commands := []string{"uname -a", "sleep 1", "pwd", "whoami"}delim := "********--------========12345678"for _, cmd := range commands {&nbsp; &nbsp; _, err = stdin.Write([]byte("echo " + delim + "\n"))&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; log.Fatal(err)&nbsp; &nbsp; }&nbsp; &nbsp; _, err := stdin.Write([]byte(cmd + "\n"))&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; log.Fatal(err)&nbsp; &nbsp; }}_, err = stdin.Write([]byte("exit\n"))if err != nil {&nbsp; &nbsp; log.Fatal(err)}err = session.Wait() // Wait for session to exitif err != nil {&nbsp; &nbsp; log.Fatal(err)}ans := strings.Split(sb.String(), delim)ans = ans[1:] // remove ssh greetings

翻过高山走不出你

检查一下:https&nbsp;://github.com/yahoo/vssh 您可以将会话设置为需要同时运行多少个命令,然后通过 run 方法将每个命令发送到远程主机并单独获取结果!
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go