从Go中进行分页输出

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

Paging output from Go

问题

我正在尝试使用golang从stdout打印输出,使用$PAGER或手动调用moreless,以便用户可以轻松地滚动查看大量选项。我该如何实现这个功能?

英文:

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 (
        &quot;io&quot;
        &quot;log&quot;
        &quot;os&quot;
        &quot;os/exec&quot;
)

func main() {
        var out io.WriteCloser
        var cmd *exec.Cmd

        if len(os.Args) != 2 {
                log.Fatal(&quot;Wrong number of args: gcat &lt;file&gt;&quot;)
        }
        fileName := os.Args[1]
        file, err := os.Open(fileName)
        if err != nil {
                log.Fatal(&quot;Error opening file: &quot;, err)
        }

        pager := os.Getenv(&quot;PAGER&quot;)

        if pager != &quot;&quot; {
                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(&quot;Unable to start $PAGER: &quot;, 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(&quot;Error waiting for cmd: &quot;, err)
                }
        }

}

答案4

得分: 1

这个版本创建了一个名为pagerio.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 (
	&quot;fmt&quot;
	&quot;io&quot;
	&quot;log&quot;
	&quot;os&quot;
	&quot;os/exec&quot;
)

var pager io.WriteCloser

func main() {
	var cmd *exec.Cmd
	cmd, pager = runPager()
	defer func() {
		pager.Close()
		cmd.Wait()
	}()
	fmt.Fprintln(pager, &quot;Hello, 世界&quot;)
}

func runPager() (*exec.Cmd, io.WriteCloser) {
	pager := os.Getenv(&quot;PAGER&quot;)
	if pager == &quot;&quot; {
		pager = &quot;more&quot;
	}
	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
}

huangapple
  • 本文由 发表于 2015年2月25日 04:33:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/28705716.html
匿名

发表评论

匿名网友

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

确定