如何使用 Go 通过 SSH 发送终端转义序列?
How can I send terminal escape sequences through SSH with Go?
我正在编写一个 Go 程序,它将使用本机 x/crypto/ssh 库通过 SSH 连接到主机并删除交互式 shell.
我正在使用 RequestPty()
,但远程端的 (bash) shell 与控制代码的行为不符合预期。
当我输入各种控制字符时,我的终端会回显它们:
$ ^[[A
字符仍然 工作 在某种意义上,如果我在按下向上箭头后按 enter,上一个命令是 运行 - 但控制字符输出破坏了什么应该显示在那里。选项卡也是如此。
有没有一些简单的方法可以让它工作?当我过去实现类似的系统时,这不是问题,因为我刚刚 shell 编辑到 openssh
,进程组的语义将它全部解决。
我研究过“The TTY Demystified”,尽管它很厉害,但不清楚从哪里开始。
我想调查的几件事:
- enable raw mode in my terminal - 但我不想在不了解原因的情况下这样做,而且 似乎 做正确的事
- fiddle with the terminal mode flags
openssh
本身必须正确地完成这项工作,但它确实是值得研究的最佳代码库。
我并不清楚这个打印是由我的本地终端仿真器或 shell 还是由远程主机上的代码完成的。
我从哪里开始?
这是我的代码示例:
conf := ssh.ClientConfig{
User: myuser,
Auth: []ssh.AuthMethod{ssh.Password(my_password)}
}
conn, err := ssh.Dial("tcp", myhost, conf)
if err != nil {
return err
}
defer conn.Close()
session, err := conn.NewSession()
if err != nil {
return err
}
defer session.Close()
session.Stdout = os.Stdout
session.Stderr = os.Stderr
session.Stdin = os.Stdin
modes := ssh.TerminalModes{
ssh.ECHO: 0
ssh.TTY_OP_ISPEED: 14400,
ssh.TTY_OP_OSPEED: 14400
}
if err := session.RequestPty("xterm", 80, 40, modes); err != nil {
return err
}
if err = session.Shell(); err != nil {
return err
}
return session.Wait()
我已经尝试使用 xterm
以外的术语值:screen-256color
和 vt100
。
郑重声明 - 在实际代码中,我有一个 for/select
循环,它捕捉到进程的各种信号并将它们发送到 Session
.
禁用 ECHOCTL
终端模式。
modes := ssh.TerminalModes{
ssh.ECHO: 0,
ssh.ECHOCTL: 0,
ssh.TTY_OP_ISPEED: 14400,
ssh.TTY_OP_OSPEED: 14400
}
ssh.ECHO: 0
和 ssh.ECHOCTL: 0
设置对我不起作用。对于 运行 遇到此问题的其他人,以下是使用 Go ssh 库获得完全工作的交互式终端所需的粗略代码:
config := return &ssh.ClientConfig{
User: "username",
Auth: []ssh.AuthMethod{ssh.Password("password")},
}
client, err := ssh.Dial("tcp", "12.34.56.78:22", config)
if err != nil { ... }
defer client.Close()
session, err := client.NewSession()
if err != nil { ... }
defer session.Close()
session.Stdout = os.Stdout
session.Stderr = os.Stderr
session.Stdin = os.Stdin
modes := ssh.TerminalModes{
ssh.ECHO: 1, // enable echoing
ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
}
fileDescriptor := int(os.Stdin.Fd())
if terminal.IsTerminal(fileDescriptor) {
originalState, err := terminal.MakeRaw(fileDescriptor)
if err != nil { ... }
defer terminal.Restore(fileDescriptor, originalState)
termWidth, termHeight, err := terminal.GetSize(fileDescriptor)
if err != nil { ... }
err = session.RequestPty("xterm-256color", termHeight, termWidth, modes)
if err != nil { ... }
}
err = session.Shell()
if err != nil { ... }
// You should now be connected via SSH with a fully-interactive terminal
// This call blocks until the user exits the session (e.g. via CTRL + D)
session.Wait()
请注意,所有键盘功能(Tab 完成、向上箭头)和信号处理(CTRL + C
、CTRL + D
)都可以在上述设置下正常工作。
我正在编写一个 Go 程序,它将使用本机 x/crypto/ssh 库通过 SSH 连接到主机并删除交互式 shell.
我正在使用 RequestPty()
,但远程端的 (bash) shell 与控制代码的行为不符合预期。
当我输入各种控制字符时,我的终端会回显它们:
$ ^[[A
字符仍然 工作 在某种意义上,如果我在按下向上箭头后按 enter,上一个命令是 运行 - 但控制字符输出破坏了什么应该显示在那里。选项卡也是如此。
有没有一些简单的方法可以让它工作?当我过去实现类似的系统时,这不是问题,因为我刚刚 shell 编辑到 openssh
,进程组的语义将它全部解决。
我研究过“The TTY Demystified”,尽管它很厉害,但不清楚从哪里开始。
我想调查的几件事:
- enable raw mode in my terminal - 但我不想在不了解原因的情况下这样做,而且 似乎 做正确的事
- fiddle with the terminal mode flags
openssh
本身必须正确地完成这项工作,但它确实是值得研究的最佳代码库。
我并不清楚这个打印是由我的本地终端仿真器或 shell 还是由远程主机上的代码完成的。
我从哪里开始?
这是我的代码示例:
conf := ssh.ClientConfig{
User: myuser,
Auth: []ssh.AuthMethod{ssh.Password(my_password)}
}
conn, err := ssh.Dial("tcp", myhost, conf)
if err != nil {
return err
}
defer conn.Close()
session, err := conn.NewSession()
if err != nil {
return err
}
defer session.Close()
session.Stdout = os.Stdout
session.Stderr = os.Stderr
session.Stdin = os.Stdin
modes := ssh.TerminalModes{
ssh.ECHO: 0
ssh.TTY_OP_ISPEED: 14400,
ssh.TTY_OP_OSPEED: 14400
}
if err := session.RequestPty("xterm", 80, 40, modes); err != nil {
return err
}
if err = session.Shell(); err != nil {
return err
}
return session.Wait()
我已经尝试使用 xterm
以外的术语值:screen-256color
和 vt100
。
郑重声明 - 在实际代码中,我有一个 for/select
循环,它捕捉到进程的各种信号并将它们发送到 Session
.
禁用 ECHOCTL
终端模式。
modes := ssh.TerminalModes{
ssh.ECHO: 0,
ssh.ECHOCTL: 0,
ssh.TTY_OP_ISPEED: 14400,
ssh.TTY_OP_OSPEED: 14400
}
ssh.ECHO: 0
和 ssh.ECHOCTL: 0
设置对我不起作用。对于 运行 遇到此问题的其他人,以下是使用 Go ssh 库获得完全工作的交互式终端所需的粗略代码:
config := return &ssh.ClientConfig{
User: "username",
Auth: []ssh.AuthMethod{ssh.Password("password")},
}
client, err := ssh.Dial("tcp", "12.34.56.78:22", config)
if err != nil { ... }
defer client.Close()
session, err := client.NewSession()
if err != nil { ... }
defer session.Close()
session.Stdout = os.Stdout
session.Stderr = os.Stderr
session.Stdin = os.Stdin
modes := ssh.TerminalModes{
ssh.ECHO: 1, // enable echoing
ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
}
fileDescriptor := int(os.Stdin.Fd())
if terminal.IsTerminal(fileDescriptor) {
originalState, err := terminal.MakeRaw(fileDescriptor)
if err != nil { ... }
defer terminal.Restore(fileDescriptor, originalState)
termWidth, termHeight, err := terminal.GetSize(fileDescriptor)
if err != nil { ... }
err = session.RequestPty("xterm-256color", termHeight, termWidth, modes)
if err != nil { ... }
}
err = session.Shell()
if err != nil { ... }
// You should now be connected via SSH with a fully-interactive terminal
// This call blocks until the user exits the session (e.g. via CTRL + D)
session.Wait()
请注意,所有键盘功能(Tab 完成、向上箭头)和信号处理(CTRL + C
、CTRL + D
)都可以在上述设置下正常工作。