30

如何使用 Golang 获得 tty 大小?我正在尝试通过执行stty size命令来做到这一点,但我无法正确编写代码。

package main

import (
  "os/exec"
  "fmt"
  "log"
)

func main() {
  out, err := exec.Command("stty", "size").Output()
  fmt.Printf("out: %#v\n", out)
  fmt.Printf("err: %#v\n", err)
  if err != nil {
    log.Fatal(err)
  }
}

输出:

out: []byte{}
err: &exec.ExitError{ProcessState:(*os.ProcessState)(0xc200066520)}
2013/05/16 02:35:57 exit status 1
exit status 1

我认为这是因为 Go 产生了一个与当前 tty 无关的进程,它正在使用它。如何将命令与当前终端关联以获取其大小?

4

7 回答 7

28

我只是想添加一个新的答案,因为我最近遇到了这个问题。官方 ssh 包https://godoc.org/golang.org/x/crypto/ssh/terminal中有一个终端包。

该软件包提供了一种轻松获取终端大小的方法。

width, height, err := terminal.GetSize(0)

0 将是您想要大小的终端的文件描述符。要获取 fd 或您当前的终端,您可以随时执行int(os.Stdin.Fd())

在幕后,它使用系统调用来获取给定 fd 的终端大小。

于 2017-07-31T18:19:05.693 回答
26

我被困在一个类似的问题上。这就是我最终得到的。

它不使用子进程,因此在某些情况下可能是可取的。

import (
    "syscall"
    "unsafe"
)

type winsize struct {
    Row    uint16
    Col    uint16
    Xpixel uint16
    Ypixel uint16
}

func getWidth() uint {
    ws := &winsize{}
    retCode, _, errno := syscall.Syscall(syscall.SYS_IOCTL,
        uintptr(syscall.Stdin),
        uintptr(syscall.TIOCGWINSZ),
        uintptr(unsafe.Pointer(ws)))

    if int(retCode) == -1 {
        panic(errno)
    }
    return uint(ws.Col)
}
于 2013-05-15T23:35:54.130 回答
24

如果您让子进程访问父进程,它会起作用stdin

package main

import (
  "os/exec"
  "fmt"
  "log"
  "os"
)

func main() {
  cmd := exec.Command("stty", "size")
  cmd.Stdin = os.Stdin
  out, err := cmd.Output()
  fmt.Printf("out: %#v\n", string(out))
  fmt.Printf("err: %#v\n", err)
  if err != nil {
    log.Fatal(err)
  }
}

产量:

out: "36 118\n"
err: <nil>
于 2013-05-15T15:57:58.310 回答
13

您可以使用golang.org/x/term包(https://pkg.go.dev/golang.org/x/term

例子

package main

import "golang.org/x/term"

func main() {
    if term.IsTerminal(0) {
        println("in a term")
    } else {
        println("not in a term")
    }
    width, height, err := term.GetSize(0)
    if err != nil {
        return
    }
    println("width:", width, "height:", height)
}

输出

in a term
width: 228 height: 27
于 2021-04-14T07:49:47.660 回答
6

如果有人有兴趣,我制作了一个包以使这更容易。

https://github.com/wayneashleyberry/terminal-dimensions

package main

import (
    "fmt"

    terminal "github.com/wayneashleyberry/terminal-dimensions"
)

func main() {
    x, _ := terminal.Width()
    y, _ := terminal.Height()
    fmt.Printf("Terminal is %d wide and %d high", x, y)
}
于 2016-09-23T13:13:35.683 回答
5

由于这里还没有其他人提出可以在WindowsUnix上运行的跨平台解决方案,因此我继续构建了一个支持两者的库。

https://github.com/nathan-fiscaletti/consolesize-go

package main

import (
    "fmt"

    "github.com/nathan-fiscaletti/consolesize-go"
)

func main() {
    cols, rows := consolesize.GetConsoleSize()
    fmt.Printf("Rows: %v, Cols: %v\n", rows, cols)
}
于 2020-08-13T18:26:32.257 回答
0

我有一个使用tcell模块的实现,在引擎盖下它仍然会使用基于调用 native 的方法dlls,但是如果您正在搜索终端尺寸,那么您很有可能仍然需要该包:

package main

import (
    "fmt"
    "github.com/gdamore/tcell"
)

func main() {
    screen, _ := tcell.NewScreen()
    screen.Init()
    
    w, h := screen.Size()
    fmt.Println(w, h)
}
于 2020-11-08T23:29:27.123 回答