Получите размер терминала в Go

Как получить размер 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, с которым он работает. Как я могу связать команду с текущим терминалом, чтобы получить его размер?

Ответ 1

Он работает, если вы предоставите дочернему процессу доступ к родительскому 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>

Ответ 2

Я столкнулся с подобной проблемой. Вот что я закончил.

Он не использует подпроцесс, поэтому может быть желательно в некоторых ситуациях.

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)
}

Ответ 3

Я просто хотел добавить новый ответ, так как недавно столкнулся с этой проблемой. Существует пакет терминала, который находится внутри официального пакета ssh https://godoc.org/golang.org/x/crypto/ssh/terminal.

Этот пакет предоставляет метод, позволяющий легко получить размер терминала.

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

0 будет файловым дескриптором терминала, размер которого вы хотите. Чтобы получить fd или ваш текущий терминал, вы всегда можете сделать int(os.Stdin.Fd())

Под прикрытием он использует системный вызов для получения размера терминала для данного файла.

Ответ 4

Если кому-то интересно, я сделал пакет, чтобы сделать это проще.

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)
}