在Go中获取终端大小

huangapple go评论101阅读模式
英文:

Get terminal size in Go

问题

如何使用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无关的进程。我如何将命令与当前终端相关联以获取其大小?

英文:

How to get tty size with Golang? I am trying do this with executing stty size command, but I can't craft code right.

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

Output:

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

I think this is because Go spawns a process not related to the current tty, with which it is working. How can I relate the command to current terminal in order to get its size?

答案1

得分: 36

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

这个包提供了一种简单获取终端大小的方法。

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

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

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

英文:

I just wanted to add a new answer since I ran into this problem recently. There is a terminal package which lives inside the official ssh package https://godoc.org/golang.org/x/crypto/ssh/terminal.

This package provides a method to easily get the size of a terminal.

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

0 would be the file descriptor of the terminal you want the size of. To get the fd or you current terminal you can always do int(os.Stdin.Fd())

Under the covers it uses a syscall to get the terminal size for the given fd.

答案2

得分: 27

我在一个类似的问题上遇到了困难。这是我最终得到的解决方案。

它不使用子进程,所以在某些情况下可能更可取。

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)
}
英文:

I was stuck on a similar problem. Here is what I ended up with.

It doesn't use a subprocess, so might be desirable in some situations.

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

得分: 25

它可以工作,如果你给子进程访问父进程的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>
英文:

It works if you give the child process access to the parent's stdin:

package main

import (
  &quot;os/exec&quot;
  &quot;fmt&quot;
  &quot;log&quot;
  &quot;os&quot;
)

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

Yields:

out: &quot;36 118\n&quot;
err: &lt;nil&gt;

答案4

得分: 25

你可以使用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("在终端中")
	} else {
		println("不在终端中")
	}
	width, height, err := term.GetSize(0)
	if err != nil {
		return
	}
	println("宽度:", width, "高度:", height)
}

输出

在终端中
宽度: 228 高度: 27
英文:

You can use golang.org/x/term package (https://pkg.go.dev/golang.org/x/term)

Example

package main

import &quot;golang.org/x/term&quot;

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

Output

in a term
width: 228 height: 27

答案5

得分: 9

由于这里没有其他人提供一个适用于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)
}
英文:

Since no one else here has yet to present a cross-platform solution that will work on both Windows and Unix, I went ahead and put together a library that supports both.

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

package main

import (
    &quot;fmt&quot;

    &quot;github.com/nathan-fiscaletti/consolesize-go&quot;
)

func main() {
    cols, rows := consolesize.GetConsoleSize()
    fmt.Printf(&quot;Rows: %v, Cols: %v\n&quot;, rows, cols)
}

答案6

得分: 5

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

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("终端宽度为 %d,高度为 %d", x, y)
}
英文:

If anyone's interested I made a package to make this easier.

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

package main

import (
    &quot;fmt&quot;

    terminal &quot;github.com/wayneashleyberry/terminal-dimensions&quot;
)

func main() {
    x, _ := terminal.Width()
    y, _ := terminal.Height()
    fmt.Printf(&quot;Terminal is %d wide and %d high&quot;, x, y)
}

答案7

得分: 1

我有一个使用tcell模块的实现,它在底层仍然使用基于调用本地dlls的方法,但如果你正在寻找终端尺寸,那么你很有可能需要这个包:

package main

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

func main() {
	screen, _ := tcell.NewScreen()
	screen.Init()
	
	w, h := screen.Size()
	fmt.Println(w, h)
}
英文:

I have one implementation that uses tcell module, under the hood it will still use approach that based on calling native dlls, but if you're searching for terminal dimensions there is a great chance that you would need that package anyway:

package main

import (
	&quot;fmt&quot;
	&quot;github.com/gdamore/tcell&quot;
)

func main() {
	screen, _ := tcell.NewScreen()
	screen.Init()
	
	w, h := screen.Size()
	fmt.Println(w, h)
}

huangapple
  • 本文由 发表于 2013年5月15日 23:40:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/16569433.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定