输入ssh提示数据

So I'm able to ssh into the machine, but i'm having trouble entering data into the prompt.

...
    sshConfig := &ssh.ClientConfig{
        User: user,
        Auth: []ssh.AuthMethod{
            ssh.Password(password),
        },
        HostKeyCallback: KeyPrint,
    }

    connection, err := ssh.Dial("tcp", connStr, sshConfig)
    if err != nil {

        log.Fatalln(err)
    }

    session, err := connection.NewSession()
    if err != nil {
        log.Fatalln(err)
    }

    modes := ssh.TerminalModes{
        ssh.ECHO:          0,     // disable echoing
        ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
        ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
    }

    if err := session.RequestPty("xterm", 80, 40, modes); err != nil {
        session.Close()
        log.Fatalf("request for pseudo terminal failed: %s", err)
    }

    stdin, err := session.StdinPipe()
    if err != nil {
        log.Fatalf("Unable to setup stdin for session: %v", err)
    }
    go io.Copy(stdin, os.Stdin)

    stdout, err := session.StdoutPipe()
    if err != nil {
        log.Fatalf("Unable to setup stdout for session: %v", err)
    }
    go io.Copy(os.Stdout, stdout)

    stderr, err := session.StderrPipe()
    if err != nil {
        log.Fatalf("Unable to setup stderr for session: %v", err)
    }
    go io.Copy(os.Stderr, stderr)

    // err = session.Run("1")

    session.Run("") // running it allows me to interact with the remote machines terminal in my own terminal.. session.Start("") exits and session.Wait() doesn't display the Welcome screen that normally greats users, and the prompt doesn't appear.

    stdin.Write([]byte("10000"))

    os.Stdin.WriteString("110000")

    // log.Fatalln(n, err)
    // os.Stdin.WriteString("1")
    // for {
    //  session.Run("1")
    //  go os.Stdin.WriteString("1")
    //  go stdin.Write([]byte("10000"))

    // }
...

The above code snippet gets me into the machine and the machine's prompt is displayed on my screen as if I ssh'ed into manually. I can type in the shell... but i need to be able to have Go type in the shell for me. The prompt that I'm interacting with is a text based game so I can't just issue commands e.g no (ls, echo, grep, etc..) the only thing I'm allow to pass in are numbers. How do I send input to the ssh session? I've tried many ways and none of the input seems to be going through.

I'm also attaching a screenshot of the prompt, just incase the description above is confusion in trying to portray the type of session this is.

ssh session screen

UPDATE: I think I've found a way to send the data, at least once.

session, err := connection.NewSession()
    if err != nil {
        log.Fatalln(err)
    }

    // ---------------------------------

    modes := ssh.TerminalModes{
        ssh.ECHO:          0,     // disable echoing
        ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
        ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
    }

    if err := session.RequestPty("xterm", 80, 40, modes); err != nil {
        session.Close()
        log.Fatalf("request for pseudo terminal failed: %s", err)
    }

    stdin, err := session.StdinPipe()
    if err != nil {
        log.Fatalf("Unable to setup stdin for session: %v", err)
    }
    go io.Copy(stdin, os.Stdin)

    stdout, err := session.StdoutPipe()
    if err != nil {
        log.Fatalf("Unable to setup stdout for session: %v", err)
    }
    go io.Copy(os.Stdout, stdout)

    stderr, err := session.StderrPipe()
    if err != nil {
        log.Fatalf("Unable to setup stderr for session: %v", err)
    }
    go io.Copy(os.Stderr, stderr)

    go session.Start("")
    for {
        stdin.Write([]byte("10000000
"))
        break
    }
    session.Wait()

I start the session with go session.Start("") remember that there is no point is passing in command because all i'm doing is entering data in response to a prompt. I then use session.Wait() at the end of a for loop...kinda like one does when using channels and waitgroups.. inside the for loop i send data with stdin.Write([]byte("10000000 ")) where the important thing to note is to add the delimiter to simulate hitting enter on the keyboard..

If there are any better ways to achieve what i'm trying to achieve please feel free. Next steps are to parse the stdout for a response and reply accordingly.

ssh
duanbei7005
duanbei7005 告诉我你是否设法使它工作
大约 3 年之前 回复
dongzhi4470
dongzhi4470 谢谢,我会尝试它的任何帮助它很好的帮助:-)
大约 3 年之前 回复
duancong2160
duancong2160 好的,您似乎比我更了解这一点。除了建议使用io.MultiWriter(writers...io.Writer)io.Writer,我认为我没有太大帮助,但是我不确定这是否有任何帮助。
大约 3 年之前 回复
doujie7346
doujie7346 并非100%,但是我上面发布的链接显示了如何输入密码,或者我帖子中的代码显示了如何提供密码,至少您可以使用这些密码来找到自己的问题的解决方案。我的更像是...我可以的。服务器提示我。服务器要求我输入一个数字....那就到了地狱。我还没有弄清楚如何用go输入数字。
大约 3 年之前 回复
doushu8260
doushu8260 哦,是的,我明白了。我正在做一个项目,最近几天我也一直在使用SSH软件包,我想我知道您的问题是什么。我当时遇到同样的问题。我的go程序运行一个脚本,但是我想用sudo...运行它,唯一的问题是,在以sudo执行我的命令的过程中运行sudo...之后,它会要求用户输入密码,但不会完成执行。我不确定在发送命令后如何输入密码。这是您遇到的同一种问题吗?
大约 3 年之前 回复
doru52911
doru52911 我还看到了您可能在哪里使用扫描仪的想法stackoverflow.com/q/23019890/4639336,但是在这种情况下,我没有输入shell命令。我只是通过ssh与游戏互动。因此终端命令对此不起作用。我需要一种将按键数据发送到游戏外壳的方法。换句话说,当我运行代码时,我可以在外壳上与游戏进行交互,就像我手动将其切入游戏中一样。Go应该与其进行交互,而不是与我的终端和键盘进行交互。您知道,自动化就像硒一样。
大约 3 年之前 回复
douyong4842
douyong4842 这是一个ssh会话。ssh会话内部是一个正在运行的程序。这不是我的程序正在运行。我不等待用户输入。我是用户,已连接到远程终端。目标是解析标准输出,确定我的算法应如何响应,然后提交答案以在30秒内赢得10场比赛。这意味着我不能只是远程输入自己在键盘上输入内容。我的程序通过ssh登录到远程计算机,必须解析stdout的响应并为我输入数字。因此没有fmt扫描。
大约 3 年之前 回复
duanbo2048
duanbo2048 您不能只使用fmt.Scanln(...)接收用户输入,然后执行程序中需要的任何命令?
大约 3 年之前 回复

1个回答

An empty Start will work, however within the ssh package, Start, Run, and Shell are all calls to, basically, the same thing. Start("cmd") executes a command within a shell, Run("cmd") is a simple call to Start("cmd") that then invokes Wait() for you (giving the feel of executing without concurrency), and Shell opens a shell (like Start), but without a command passed. It's six of one, half a dozen of the other, really, but using Shell() is probably the cleanest way to go about that.

Also, bear in mind that Start() and Shell() both leverage concurrency without the explicit invocation of "go". It might free an additional millisecond or so to invoke concurrency manually, but if that isn't of significance to you, then you should be able to drop that. The automatic concurrency of Start and Shell is the reason for the Wait() and Run("cmd") methods.

If you have no need to interpret your output (stdout/err), then you can map these without the pipe() call or io.Copy(), which is easier and more efficient. I did this in the example below, but bear in mind that if you do interpret the output, it's probably easier to work with the Pipe(). You can send multiple commands (or numbers) sequentially without reading for a prompt in most cases, but some things (like passwords prompts) clear the input buffer. If this happens for you, then you'll need to read the Stdout to find your prompt or leverage an expect tool like goexpect (https://github.com/google/goexpect). There are several expect-like packages for Go, but this one is from Google and (as of this posting) still fairly recently maintained.

StdinPipe() exposes a writeCloser that can be leveraged without io.Copy(), which should be more efficient.

Your for loop that writes to the StdinPipe() should allow you to enter several commands (or in your case, sets of numbers)... as an example, I have this reading commands (numbers, etc) from os.Args and iterating through them.

Lastly, you should probably add a session.Close()for healthy completion (you already have a call for errors). That said, this is what I would recommend (based on your last example):

modes := ssh.TerminalModes{
    ssh.ECHO:          0,     // disable echoing
    ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
    ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
}

if err := session.RequestPty("xterm", 80, 40, modes); err != nil {
    session.Close()
    log.Fatalf("request for pseudo terminal failed: %s", err)
}
defer session.Close()

stdin, err := session.StdinPipe()
if err != nil {
    log.Fatalf("Unable to setup stdin for session: %v", err)
}

session.Stdout = os.Stdout
session.Stderr = os.Stderr

err = session.Shell()
if err != nil {
    log.Fatalf("Unable to setup stdin for session: %v", err)
}
for _, cmd := range os.Args {
    stdin.Write([]byte(cmd + "
"))
}

session.Wait()

Oh, one more item to note is that the Wait() method relies on an unchecked channel that retrieves the exitStatus from your command, and this does hang on rare occasion (it is particularly problematic when connecting to cisco gear, but can happen with others as well). If you find that you encounter this (or you'd just like to be careful), you might want to wrap Wait() inside of some sort of timeout methodology such as by invoking Wait() with concurrency and reading the response through a channel that can be cased along with time.After() (I can provide an example, if that would be helpful).

Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问