这个问题是关于golang.org/x/crypto/ssh
包,也许是伪终端行为。
代码
这是演示代码。您可以在本地计算机上运行它,只需更改凭据即可访问SSH。
package main
import (
"bufio"
"fmt"
"golang.org/x/crypto/ssh"
"io"
)
func main() {
var pipe io.Reader
whichPipe := "error" // error or out
address := "192.168.1.62:22"
username := "username"
password := "password"
sshConfig := &ssh.ClientConfig{
User: username,
Auth: []ssh.AuthMethod{ssh.Password(password)},
}
connection, err := ssh.Dial("tcp", address, sshConfig)
if err != nil {
panic(err)
}
session, err := connection.NewSession()
if err != nil {
panic(err)
}
modes := ssh.TerminalModes{
ssh.ECHO: 0,
ssh.ECHOCTL: 0,
ssh.TTY_OP_ISPEED: 14400,
ssh.TTY_OP_OSPEED: 14400,
}
if err := session.RequestPty("xterm", 80, 0, modes); err != nil {
session.Close()
panic(err)
}
switch whichPipe {
case "error":
pipe, _ = session.StderrPipe()
case "out":
pipe, _ = session.StdoutPipe()
}
err = session.Run("whoami23")
scanner := bufio.NewScanner(pipe)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}
实际结果
空行
预期结果
bash: whoami23: command not found
当前“解决方案”
要获得预期结果,您有两种选择:
whichPipe
值更改为out
。是的,如果您使用tty
,所有错误都会转到stdout。session.RequestPty
。但在我的情况下,我需要运行sudo
命令,这些命令需要tty
(服务器不在我的控制范围内,因此我无法禁用此要求)。我用第三种方式。我从err
检查err = session.Run("whoami23")
,如果不是nil
,我将session.StdoutPipe()
的内容标记为STDERR。
但这种方法有限制。例如,如果我运行类似sudo sh -c 'uname -r; whoami23;'
的内容,则整个结果将被标记为错误,而uname -r
将输出返回到STDOUT。
问题
虽然这个行为对我来说是合乎逻辑的(SSH客户端从pty看到的所有东西都是没有差异的输出)但我仍然不确定我是否会错过某些东西,并且有一个技巧可以分割这些输出。