【问题标题】:Terminal in golang: syscall vs os/exec sttygolang 中的终端:syscall vs os/exec stty
【发布时间】:2018-12-01 19:55:55
【问题描述】:

这是如何通过 go 中的系统调用获得 (POSIX) 终端大小的:

func getTermDim() (width, height int, err error) {
    var termDim [4]uint16
    if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(0), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&termDim)), 0, 0, 0); err != 0 {
        return -1, -1, err
    }
    return int(termDim[1]), int(termDim[0]), nil
}

现在,同样的事情,用 os/exec 调用 stty:

func getTermDim() (width, height int, err error) {
        cmd := exec.Command("stty", "size")
        cmd.Stdin = os.Stdin
        var termDim []byte
        if termDim, err = cmd.Output(); err != nil {
                return
        }
        fmt.Sscan(string(termDim), &height, &width)
        return
}

在实践中,第一个解决方案可能会变得非常繁重且难以阅读,当一个人必须将终端置于原始模式、设置选项等时。当一个人习惯于 stty(例如在 shell 脚本中)时,第二个解决方案就是这样容易多了!

所以我的问题是:使用第一个解决方案有什么好处?是速度?是不是我们不能依赖 stty 命令安装在宿主机上?还有什么我没想到的?

简而言之,使用 stty 与系统调用的“风险”或“成本”是什么?

【问题讨论】:

  • IMO 系统调用版本更加清晰,唯一的改进是我将它包装成一个通用函数,该函数将模拟 ioctl 接口(当然是 Go 风格),然后是 @ 987654326@ 会打电话给那个。
  • 另一种方法是不要重新发明轮子,而是抓住golang.org/x/crypto/ssh/terminalgithub.com/nsf/termbox-go 来利用他们使用终端的能力。
  • 我们对更清楚的事情的分歧应该来自这样一个事实,我有点知道stty 做了什么,因此改为调用它或做它应该做的事情直接是只是粗鲁。对于那些习惯使用 shell 脚本的人来说,调用stty 应该更合乎逻辑:-)
  • 感谢您的意见,kostix。看来这最终取决于个人喜好。
  • 我有另一个案例 pro 系统调用:如果你的程序的标准输出没有连接到 TTY,系统调用允许更明智的错误处理,否则你必须解析 stty的输出并依靠它在不同的可能平台上保持稳定(我对此表示怀疑)。

标签: go terminal system-calls stty


【解决方案1】:

关于风险:

  • stty:如果stty 命令在$PATH 中不可用,您的程序将无法正常工作。或者如果$PATH 中的stty 命令不是您所期望的(这可能是一个安全问题)。或者,如果程序在占用空间极少的 Docker 容器中运行:您必须将 stty 放入映像中。
  • 系统调用:您的程序依赖于操作系统。只要您将该函数写入受build tags 保护的文件中,以确保在不受支持的操作系统上编译时构建将失败,就可以了。

关于性能,只需使用包testing 写一个benchmark。但我已经可以告诉你,exec.Command 意味着多个系统调用比IOCTL/TIOCGWINSZ 更昂贵。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-12-12
    • 1970-01-01
    • 2014-03-11
    • 2011-05-30
    • 1970-01-01
    • 2019-09-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多