英文:
Paging output from Go
问题
我正在尝试使用golang从stdout打印输出,使用$PAGER
或手动调用more
或less
,以便用户可以轻松地滚动查看大量选项。我该如何实现这个功能?
英文:
I'm trying print to stdout from golang using $PAGER
or manually invoking more
or less
to allow the user to easily scroll through a lot of options. How can I achieve this?
答案1
得分: 5
你可以使用 os/exec 包来启动一个运行 less
(或者 $PAGER
中的任何内容)的进程,然后将一个字符串传递给它的标准输入。以下是我测试通过的代码:
func main() {
// 可以读取 $PAGER 而不是硬编码路径。
cmd := exec.Command("/usr/bin/less")
// 将要显示的字符串传递给它。
cmd.Stdin = strings.NewReader("你想要显示的文本。")
// 这一步很关键 - 否则它会写入一个空设备。
cmd.Stdout = os.Stdout
// 分离一个进程并等待其终止。
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
}
英文:
You can use the os/exec package to start a process that runs less
(or whatever is in $PAGER
) and then pipe a string to its standard input. The following worked for me:
func main() {
// Could read $PAGER rather than hardcoding the path.
cmd := exec.Command("/usr/bin/less")
// Feed it with the string you want to display.
cmd.Stdin = strings.NewReader("The text you want to show.")
// This is crucial - otherwise it will write to a null device.
cmd.Stdout = os.Stdout
// Fork off a process and wait for it to terminate.
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
}
答案2
得分: 3
我假设你已经有一个程序将输出打印到标准输出(stdout),你想要捕获并发送到你的分页器(pager),而不想重写输入/输出以使用另一个输入流,就像其他回答所要求的那样。
你可以创建一个os.Pipe,它的工作方式类似于使用" | less"运行命令,将一个端口连接到你的分页器,将另一个端口连接到标准输出,像这样:
// 创建一个供分页器使用的管道
r, w, err := os.Pipe()
if err != nil {
panic("你可能希望以更优雅的方式处理错误")
}
// 捕获标准输出以供分页器使用。保留旧值,以便稍后恢复。
stdout := os.Stdout
os.Stdout = w
// 创建分页器进程并连接适当的输入/输出流。
pager := exec.Command("less")
pager.Stdin = r
pager.Stdout = stdout // 分页器使用原始的标准输出,而不是管道
pager.Stderr = os.Stderr
// 延迟一个函数,关闭管道并调用分页器,然后在此函数返回并完成输出捕获后恢复os.Stdout。
//
// 注意非常重要的是要关闭管道,这样才能向分页器发送EOF,否则会发生奇怪的事情。
defer func() {
// 关闭管道
w.Close()
// 运行分页器
if err := pager.Run(); err != nil {
fmt.Fprintln(os.Stderr, err)
}
// 恢复标准输出
os.Stdout = stdout
}()
英文:
I assume you already have output being printed from your program to stdout that you want to have captured and sent to your pager, you don't want to
rewrite the I/O to use another input stream like the other responses require.
You can create an os.Pipe which works the same as running a command with "|less" by attaching one side to your pager and the other side to stdout like this:
// Create a pipe for a pager to use
r, w, err := os.Pipe()
if err != nil {
panic("You probably want to fail more gracefully than this")
}
// Capture STDOUT for the Pager. Keep the old
// value so we can restore it later.
stdout := os.Stdout
os.Stdout = w
// Create the pager process to execute and attach
// the appropriate I/O streams.
pager := exec.Command("less")
pager.Stdin = r
pager.Stdout = stdout // the pager uses the original stdout, not the pipe
pager.Stderr = os.Stderr
// Defer a function that closes the pipe and invokes
// the pager, then restores os.Stdout after this function
// returns and we've finished capturing output.
//
// Note that it's very important that the pipe is closed,
// so that EOF is sent to the pager, otherwise weird things
// will happen.
defer func() {
// Close the pipe
w.Close()
// Run the pager
if err := pager.Run(); err != nil {
fmt.Fprintln(os.Stderr, err)
}
// restore stdout
os.Stdout = stdout
}()
答案3
得分: 2
这是一个相对简单的示例,当设置了$PAGER
时,它会使用它来显示文件内容。
package main
import (
"io"
"log"
"os"
"os/exec"
)
func main() {
var out io.WriteCloser
var cmd *exec.Cmd
if len(os.Args) != 2 {
log.Fatal("参数错误:gcat <file>")
}
fileName := os.Args[1]
file, err := os.Open(fileName)
if err != nil {
log.Fatal("打开文件错误:", err)
}
pager := os.Getenv("PAGER")
if pager != "" {
cmd = exec.Command(pager)
var err error
out, err = cmd.StdinPipe()
if err != nil {
log.Fatal(err)
}
cmd.Stdout = os.Stdout
if err := cmd.Start(); err != nil {
log.Fatal("无法启动 $PAGER:", err)
}
} else {
out = os.Stdout
}
_, err = io.Copy(out, file)
if err != nil {
log.Fatal(err)
}
file.Close()
out.Close()
if cmd != nil {
if err := cmd.Wait(); err != nil {
log.Fatal("等待命令完成时出错:", err)
}
}
}
希望对你有帮助!
英文:
Here is a somewhat naive cat example that uses $PAGER
when set.
package main
import (
"io"
"log"
"os"
"os/exec"
)
func main() {
var out io.WriteCloser
var cmd *exec.Cmd
if len(os.Args) != 2 {
log.Fatal("Wrong number of args: gcat <file>")
}
fileName := os.Args[1]
file, err := os.Open(fileName)
if err != nil {
log.Fatal("Error opening file: ", err)
}
pager := os.Getenv("PAGER")
if pager != "" {
cmd = exec.Command(pager)
var err error
out, err = cmd.StdinPipe()
if err != nil {
log.Fatal(err)
}
cmd.Stdout = os.Stdout
if err := cmd.Start(); err != nil {
log.Fatal("Unable to start $PAGER: ", err)
}
} else {
out = os.Stdout
}
_, err = io.Copy(out, file)
if err != nil {
log.Fatal(err)
}
file.Close()
out.Close()
if cmd != nil {
if err := cmd.Wait(); err != nil {
log.Fatal("Error waiting for cmd: ", err)
}
}
}
答案4
得分: 1
这个版本创建了一个名为pager
的io.Writer
,用于所有需要分页的输出(如果你愿意,可以将其分配给os.Stdout
),并在main()
返回时正确关闭它并等待$PAGER
。
import (
"fmt"
"io"
"log"
"os"
"os/exec"
)
var pager io.WriteCloser
func main() {
var cmd *exec.Cmd
cmd, pager = runPager()
defer func() {
pager.Close()
cmd.Wait()
}()
fmt.Fprintln(pager, "Hello, 世界")
}
func runPager() (*exec.Cmd, io.WriteCloser) {
pager := os.Getenv("PAGER")
if pager == "" {
pager = "more"
}
cmd := exec.Command(pager)
out, err := cmd.StdinPipe()
if err != nil {
log.Fatal(err)
}
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Start(); err != nil {
log.Fatal(err)
}
return cmd, out
}
英文:
This version creates an io.Writer
called pager
for all the output that you want paged (you can assign it to os.Stdout
if you like) and correctly closes that and waits for the $PAGER
when main()
returns.
import (
"fmt"
"io"
"log"
"os"
"os/exec"
)
var pager io.WriteCloser
func main() {
var cmd *exec.Cmd
cmd, pager = runPager()
defer func() {
pager.Close()
cmd.Wait()
}()
fmt.Fprintln(pager, "Hello, 世界")
}
func runPager() (*exec.Cmd, io.WriteCloser) {
pager := os.Getenv("PAGER")
if pager == "" {
pager = "more"
}
cmd := exec.Command(pager)
out, err := cmd.StdinPipe()
if err != nil {
log.Fatal(err)
}
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Start(); err != nil {
log.Fatal(err)
}
return cmd, out
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论