程序逐行读取输出作为流的概念存在吗?

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

Is there a concept of reading the output of a program line by line, as a stream?

问题

我有一个shell命令(例如journalctl -f -o json),它会连续将行输出到标准输出。

我想逐行检索这个输出并进一步处理它。

os/exec的文档介绍了如何读取命令的输出,而io则处理流缓冲。

无论我在哪里查找,处理都是通过一个固定的缓冲区进行的,该缓冲区被读取、处理和进一步写入。我的问题是,这个缓冲区的大小是固定的,与内容无关。

有没有一种方法可以逐行读取输入流(在我的情况下是shell命令的输出)?可能使用比io读取器更高级的库?

英文:

I have a shell command (e.g. journalctl -f -o json) that continuously streams lines to the standard output.

I would like to retrieve this output line by line and process it further.

The documentation of os/exec addresses how to read the output of a command, and io deals with stream buffering.

Everywhere I looked, the handling goes through a fixed buffer that is read into, handled, and written further. My problem is that the size of this buffer is fixed and independent of the content.

Is there a way to read an incoming stream (in my case - the output of a shell command) line by line? Possibly with a library more high-level than io readers?

答案1

得分: 2

使用Cmd.StdoutPipe()来获取进程的输出(通过管道),在使用Cmd.Start()启动进程之前(Start()启动命令但不等待其完成)。

使用bufio.Scanner逐行读取输入(io.Reader)。

例如,我将使用以下bash脚本,它会打印当前时间3次,每次间隔1秒:

for i in {1..3}; do date; sleep 1; done

示例代码执行并逐行读取其输出:

cmd := exec.Command("bash", "-c", "for i in {1..3}; do date; sleep 1; done")
out, err := cmd.StdoutPipe()
if err != nil {
    log.Fatal(err)
}
defer out.Close()

err = cmd.Start()
if err != nil {
    log.Fatal(err)
}

scanner := bufio.NewScanner(out)
for scanner.Scan() {
    line := scanner.Text()
    fmt.Println("Output:", line)
}

示例输出:

2022/11/29 14:38:48 Output: Tue Nov 29 02:38:48 PM CET 2022
2022/11/29 14:38:49 Output: Tue Nov 29 02:38:49 PM CET 2022
2022/11/29 14:38:50 Output: Tue Nov 29 02:38:50 PM CET 2022

(每行开头的第一个日期时间来自log包,用于验证每行在延迟1秒后打印,其他时间戳是date命令的输出。)

英文:

Use Cmd.StdoutPipe() to obtain the (piped) output of the process before you start it with Cmd.Start() (Start() starts the command but does not wait for it to complete).

And use a bufio.Scanner to read an input (io.Reader) line-by-line.

For example I'm gonna use this bash script that prints the current time 3 times, sleeping 1 second between them:

for i in {1..3}; do date; sleep 1; done

Example executing this and reading its output line-by-line:

cmd := exec.Command("bash", "-c", "for i in {1..3}; do date; sleep 1; done")
out, err := cmd.StdoutPipe()
if err != nil {
	log.Fatal(err)
}
defer out.Close()

err = cmd.Start()
if err != nil {
	log.Fatal(err)
}

scanner := bufio.NewScanner(out)
for scanner.Scan() {
	line := scanner.Text()
	fmt.Println("Output:", line)
}

Example output:

2022/11/29 14:38:48 Output: Tue Nov 29 02:38:48 PM CET 2022
2022/11/29 14:38:49 Output: Tue Nov 29 02:38:49 PM CET 2022
2022/11/29 14:38:50 Output: Tue Nov 29 02:38:50 PM CET 2022

(The first date-time at the beginning of each line is from the log package, to verify each line is printed after a second delay, the other timestamp is the output of the date command.)

huangapple
  • 本文由 发表于 2022年11月29日 20:58:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/74614462.html
匿名

发表评论

匿名网友

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

确定