我的代码应该是SSH到远程主机(比如说路由器),并在远程主机上运行多个命令并返回输出。
所附代码经过简化,分为三部分:
Main
函数:读取命令列表,然后使用ExecCommands
函数拨号/ SSH到远程主机执行commands.ExecCommands
函数,获取用于SSH的远程主机IP、命令列表和SSH ClientConfig
。然后它拨号到IP,然后一个接一个地运行命令。最后,只返回一个stringInsecureClientConfig
函数中的所有命令的输出,该函数实际上除了创建一个用于ExecCommands
函数的SSH ClientConfig
之外,没有什么作用。
当我只想应用一些命令或配置并保存整个结果时,这个程序工作得很好。我的意思是,ExecCommands
接受大量命令,将它们全部推送到远程主机,并将应用命令的全部输出以一个字符串作为输出返回(或保存)。
问题:
。
目标:
ExecCommands
函数,以便为其应用的每个命令提供单独的输出。这意味着如果对于remote-host#1
应用10个命令,我应该有10个单独的字符串作为输出。Conditions/Restrictions:
H 141
中使用Run
、Shell
、Output
、Start
函数,不允许重新身份验证。例如,我只有一个可以用于所有remote-hosts.
。
点:
ExecCommands
。我把整个代码的一个简化版本给出了一个想法,stdout, err := session.StdoutPipe()
运行多个命令,这意味着-as管道-它的阅读器只有在工作完成后才能被读取。ExecCommands
函数中的for循环中使用Session.Stdout
和Session.Stdin
。尝试过,但没有成功。代码:
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
}
发布于 2020-11-19 08:20:17
由于在特殊硬件上运行的命令数量有限,并且知道每个命令的输出模式,所以可以使用strings.Split
或regexp
来拆分输出。
如果您没有echo
命令,但是知道任何具有唯一输出模式的快速响应的命令,那么您可以在下面的示例(数字2)中用echo
命令替换它。
由于会话只接受,因此调用到Run
、Start
、Shell
、Output
或CombinedOutput
,而不希望在每个命令下启动新会话:
关键是在发送命令之前使用strings.Builder
并使用sb.Reset()
将其清空,并使用io.Copy
将会话的stdout复制到strings.Builder
(假设您执行而不是需要会话的stderr):
sb := 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.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)
}
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 {
_, 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
https://stackoverflow.com/questions/62523552
复制相似问题