预先感谢大家的帮助。我在发生问题的地方标记了第41、42和58行。我无法跟踪引发错误的原因。似乎所有变量都已正确分配。我使用命令:
运行文件.go'命令''ip地址'
例如-运行file.go正常运行时间8.8.8.8
错误是:
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x54fb36]
goroutine 20 [running]:
golang.org/x/crypto/ssh.(*Client).NewSession(0x0, 0x3, 0xc42009e310, 0x10)
/home/user/go/src/golang.org/x/crypto/ssh/client.go:130 +0x26
main.executeCmd(0x7ffce5f83a51, 0x6, 0x7ffce5f83a58, 0xd, 0x5d3077, 0x2, 0xc42009a820, 0x0, 0x0)
/home/user/ssh.go:58 +0x16f
main.main.func1(0xc4200d2000, 0x7ffce5f83a51, 0x6, 0xc42009a820, 0x7ffce5f83a58, 0xd, 0x5d3077, 0x2)
/home/user/ssh.go:42 +0x7a
created by main.main
/home/user/ssh.go:41 +0x41b
package main
import (
"bytes"
"fmt"
"io/ioutil"
"log"
"os"
"time"
"golang.org/x/crypto/ssh"
)
func main() {
cmd := os.Args[1]
hosts := os.Args[2:]
results := make(chan string, 10)
timeout := time.After(10 * time.Second)
port := "22"
var hostKey ssh.PublicKey
key, err := ioutil.ReadFile("/home/user/file.pem")
if err != nil {
log.Fatalf("unable to read private key: %v", err)
}
signer, err := ssh.ParsePrivateKey(key)
if err != nil {
log.Fatalf("unable to parse private key: %v", err)
}
config := &ssh.ClientConfig{
User: "ec2-user",
Auth: []ssh.AuthMethod{
ssh.PublicKeys(signer),
},
HostKeyCallback: ssh.FixedHostKey(hostKey),
}
for _, hostname := range hosts {
go func(hostname string, port string) { // LINE 41
results <- executeCmd(cmd, hostname, port, config) // LINE 42
}(hostname, port)
}
for i := 0; i < len(hosts); i++ {
select {
case res := <-results:
fmt.Print(res)
case <-timeout:
fmt.Println("Timed out!")
return
}
}
}
func executeCmd(command, hostname string, port string, config *ssh.ClientConfig) (string, error) {
conn, err := ssh.Dial("tcp", fmt.Sprintf("%s:%s", hostname, port), config)
if err != nil {
return "", err
}
defer conn.Close()
session, err := conn.NewSession() //LINE 58
if err != nil {
return "", err
}
defer session.Close()
var stdoutBuf bytes.Buffer
session.Stdout = &stdoutBuf
if err := session.Run(command); err != nil {
return "", err
}
return fmt.Sprintf("%s -> %s", hostname, stdoutBuf.String()), nil
}
答案 0 :(得分:4)
正如@JoshuaKolden指出的那样,您需要检查ssh.Dial
发出的错误,如果值不是nil
,则不要继续。这是带有错误检查的executeCmd
函数的版本:
func executeCmd(command, hostname string, port string, config *ssh.ClientConfig) (string, error) {
conn, err := ssh.Dial("tcp", fmt.Sprintf("%s:%s", hostname, port), config)
if err != nil {
return "", err
}
defer conn.Close()
session, err := conn.NewSession() //LINE 58
if err != nil {
return "", err
}
defer session.Close()
var stdoutBuf bytes.Buffer
session.Stdout = &stdoutBuf
if err := session.Run(command); err != nil {
return "", err
}
return fmt.Sprintf("%s -> %s", hostname, stdoutBuf.String()), nil
}
您当然也需要调整调用executeCmd
的goroutine来进行错误处理。也许将错误消息打印到os.Stderr
上以找出问题所在。
引起恐慌的原因是,您在ssh.Dial
中遇到了一个错误,将变量conn
的值保留为nil
。如果查看ssh
包中的client.go line 130,您会发现它在指针接收器c
到OpenChannel
上进行了方法调用。通常,对于采用指针接收器的结构方法来说,这不是问题,但是在这种情况下,OpenChannel
是Conn
接口中嵌入在Client
结构中的方法。当使用nil接收器调用接口方法时,会感到恐慌。
编辑:在goroutine中使用此功能的新版本的一种潜在方法:
go func(hostname string, port string) { // LINE 41
result, err := executeCmd(cmd, hostname, port, config) // LINE 42
if err != nil {
fmt.Fprintf(os.Stderr, "error running cmd on host %s port %s: %s", hostname, port, err)
return
}
results <- result
}(hostname, port)
不幸的是,您无法在同一语句中发送到通道并分配给另一个变量。另外,您需要检查错误并进行一些处理(如果不是零的话),在这种情况下,我选择将其打印为标准错误。