【问题标题】:Golang write input and get output from terminal processGolang 写入输入并从终端进程获取输出
【发布时间】:2014-04-11 18:18:45
【问题描述】:

我有一个关于如何从终端子进程(如 ssh)发送输入和接收输出的问题。 python中的一个例子是这样的:

how to give subprocess a password and get stdout at the same time

我在 Golang 中找不到与上述工作方式类似的简单示例。

在 Golang 中我想做这样的事情,但它似乎不起作用:

    cmd := exec.Command("ssh", "user@x.x.x.x")
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    stdin, _ := cmd.StdinPipe()
    stdin.Write([]byte("password\n"))
    cmd.Run()

但是;我不确定如何在 go 中执行此操作,因为每次执行此 ssh 命令时,我只能获得输出。我无法从代码中自动输入我的密码。 有没有人有写入终端进程(如 ssh)的示例?如果有,请分享。

【问题讨论】:

  • 你不能用ssh很容易地做到这一点,因为它会拒绝从stdin读取密码。见stackoverflow.com/q/1340366。您最好的选择似乎是生成一个密钥对并使用它进行身份验证。
  • 我以前在 Python 中尝试过这样做。我只能使用Paramiko 让它工作。你应该试试go.crypto/ssh
  • 您似乎知道如何处理子进程的标准输入和标准输出就好了。问题不在您的代码中,而是由于 ssh 在检测到它没有在 shell 或 tty 中运行时具有不同的行为。您必须使用证书身份验证而不是密码。
  • cmd.Env = append(cmd.Env, 'TERM=xterm') 有帮助吗?
  • @OneOfOne 感谢您的输入,这正是我正在寻找的。感谢上帝。

标签: process terminal go pty


【解决方案1】:

感谢上面的 cmets,我能够使用密码进行 ssh 访问。我使用了 golang 的 ssh api 库。当我按照以下示例进行操作时,这相当简单:

https://code.google.com/p/go/source/browse/ssh/example_test.go?repo=crypto

具体来说:

func ExampleDial() {
    // An SSH client is represented with a ClientConn. Currently only
    // the "password" authentication method is supported.
    //
    // To authenticate with the remote server you must pass at least one
    // implementation of AuthMethod via the Auth field in ClientConfig.
    config := &ClientConfig{
            User: "username",
            Auth: []AuthMethod{
                    Password("yourpassword"),
            },
    }
    client, err := Dial("tcp", "yourserver.com:22", config)
    if err != nil {
            panic("Failed to dial: " + err.Error())
    }

    // Each ClientConn can support multiple interactive sessions,
    // represented by a Session.
    session, err := client.NewSession()
    if err != nil {
            panic("Failed to create session: " + err.Error())
    }
    defer session.Close()

    // Once a Session is created, you can execute a single command on
    // the remote side using the Run method.
    var b bytes.Buffer
    session.Stdout = &b
    if err := session.Run("/usr/bin/whoami"); err != nil {
            panic("Failed to run: " + err.Error())
    }
    fmt.Println(b.String())
}

【讨论】:

    【解决方案2】:

    这是上述示例https://godoc.org/golang.org/x/crypto/ssh#example-Dial的修改/完整版本

    首先通过go get golang.org/x/crypto/ssh获取terminal

    package main
    
    import (
        "bufio"
        "bytes"
        "fmt"
        "os"
        "strings"
    
        "golang.org/x/crypto/ssh"
        "golang.org/x/crypto/ssh/terminal"
    )
    
    func main() {
        if len(os.Args) < 3 {
            usage := "\n./remote-ssh {host} {port}"
            fmt.Println(usage)
        } else {
            host := os.Args[1]
            port := os.Args[2]
    
            username, password := credentials()
            config := &ssh.ClientConfig{
                User: username,
                Auth: []ssh.AuthMethod{
                    ssh.Password(password),
                },
            }
            connectingMsg := fmt.Sprintf("\nConnecting to %s:%v remote server...", host, port)
            fmt.Println(connectingMsg)
    
            hostAddress := strings.Join([]string{host, port}, ":")
            // fmt.Println("Host add %s ", hostAddress)
            client, err := ssh.Dial("tcp", hostAddress, config)
            if err != nil {
                panic("Failed to dial: " + err.Error())
            }
    
            for {
                session, err := client.NewSession()
                if err != nil {
                    panic("Failed to create session: " + err.Error())
                }
                defer session.Close()
    
                // Once a Session is created, can execute a single command on remote side
                var cmd string
                str := "\nEnter command (e.g. /usr/bin/whoami OR enter 'exit' to return) : "
                fmt.Print(str)
                fmt.Scanf("%s", &cmd)
                if cmd == "exit" || cmd == "EXIT" {
                    break
                }
                s := fmt.Sprintf("Wait for command '%s' run and response...", cmd)
                fmt.Println(s)
    
                var b bytes.Buffer
                session.Stdout = &b
                if err := session.Run(cmd); err != nil {
                    panic("Failed to run: " + err.Error())
                }
                fmt.Println(b.String())
            }
        }
    }
    
    func credentials() (string, string) {
        reader := bufio.NewReader(os.Stdin)
    
        fmt.Print("Enter Username: ")
        username, _ := reader.ReadString('\n')
    
        fmt.Print("Enter Password: ")
        bytePassword, err := terminal.ReadPassword(0)
        if err != nil {
            panic(err)
        }
        password := string(bytePassword)
    
        return strings.TrimSpace(username), strings.TrimSpace(password)
    }
    

    https://play.golang.org/p/4Ad1vKNXmI

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-24
      • 2013-05-09
      • 1970-01-01
      相关资源
      最近更新 更多