go函数-紧急:运行时错误:无效的内存地址或nil指针取消引用

时间:2018-08-30 22:25:10

标签: go

预先感谢大家的帮助。我在发生问题的地方标记了第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

}

1 个答案:

答案 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,您会发现它在指针接收器cOpenChannel上进行了方法调用。通常,对于采用指针接收器的结构方法来说,这不是问题,但是在这种情况下,OpenChannelConn接口中嵌入在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)

不幸的是,您无法在同一语句中发送到通道并分配给另一个变量。另外,您需要检查错误并进行一些处理(如果不是零的话),在这种情况下,我选择将其打印为标准错误。