go function - panic:运行时错误:无效的内存地址或nil指针取消引用

问题描述 投票:2回答:1

在此先感谢大家的帮助。我已经标记了出现问题的第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

}

go
1个回答
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。如果你看一下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)

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

© www.soinside.com 2019 - 2024. All rights reserved.