在此先感谢大家的帮助。我已经标记了出现问题的第41,42和58行。我无法追踪引发错误的内容。似乎所有变量都已正确分配。我使用命令:
go run file.go'command''ip address' 恩。 - 去运行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
}
正如@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
。如果你看一下client.go line 130包中的ssh
,你可以看到它对指针接收器c
进行OpenChannel
的方法调用。通常这对于采用指针接收器的struct方法来说不是问题,但在这种情况下,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)
不幸的是,您无法发送到通道并在同一语句中分配给另一个变量。此外,你需要检查错误并做一些事情,如果它不是零,在这种情况下,我选择将其打印到标准错误。