从io.ReadCloser中读取一行

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

ReadLine from io.ReadCloser

问题

我需要找到一种方法从io.ReadCloser对象中读取一行,或者找到一种方法在一个字节数组上分割一个“结束行”符号。然而,我不知道结束行符号在哪里,也找不到它。

我的应用程序执行一个php脚本,并且需要获取脚本的实时输出,并在获取到输出时对其进行处理。

这是我的代码的一小部分:

cmd := exec.Command(prog, args)   
/* cmd := exec.Command("ls")*/
out, err := cmd.StdoutPipe()
if err != nil {
  fmt.Println(err)
}
err = cmd.Start()
if err != nil {
  fmt.Println(err)
}

之后,我在一个go例程中监视out缓冲区。我尝试了两种方法。

  1. nr, er := out.Read(buf),其中buf是一个字节数组。问题在于我需要在每个新行处分割数组。
  2. 我的第二个选择是创建一个新的bufio.reader
    r := bufio.NewReader(out) line,_,e := r.ReadLine()
    如果我执行像ls这样的命令,它可以正常运行,我逐行获取输出,但是如果我执行一个php脚本,它会立即出现文件结束错误并退出(我猜这是由于php的延迟输出导致的)。

编辑:我的问题是我在go例程中创建了bufio.Reader,而如果我像minikomi建议的那样在StdoutPipe()之后立即创建它,它就可以正常工作。

英文:

I need to find a way to read a line from a io.ReadCloser object OR find a way to split a byte array on a "end line" symbol. However I don't know the end line symbol and I can't find it.

My application execs a php script and needs to get the live output from the script and do "something" with it when it gets it.

Here's a small piece of my code:

cmd := exec.Command(prog, args)   
/* cmd := exec.Command("ls")*/
out, err := cmd.StdoutPipe()
if err != nil {
  fmt.Println(err)
}
err = cmd.Start()
if err != nil {
  fmt.Println(err)
}

after this I monitor the out buffer in a go routine. I've tried 2 ways.

  1. nr, er := out.Read(buf) where buf is a byte array. the problem here is that I need to brake the array for each new line
  2. my second option is to create a new bufio.reader
    r := bufio.NewReader(out)
    line,_,e := r.ReadLine()

    it runs fine if I exec a command like ls, I get the output line by line, but if I exec a php script it immediately get an End Of File error and exits(I'm guessing that's because of the delayed output from php)

EDIT: My problem was I was creating the bufio.Reader inside the go routine whereas if I do it right after the StdoutPipe() like minikomi suggested, it works fine

答案1

得分: 11

您可以使用bufio创建一个读取器,然后读取直到下一个换行符(注意,单引号表示字符!):

stdout, err := cmd.StdoutPipe()
rd := bufio.NewReader(stdout)
if err := cmd.Start(); err != nil {
log.Fatal("缓冲错误:", err)
}

for {
str, err := rd.ReadString('\n')
if err != nil {
log.Fatal("读取错误:", err)
return
}
fmt.Println(str)
}

如果您尝试在goroutine中从读取器中读取而没有任何停止脚本的内容,它将退出。

英文:

You can create a reader using bufio, and then read until the next line break character (Note, single quotes to denote character!):

stdout, err := cmd.StdoutPipe()
rd := bufio.NewReader(stdout)
if err := cmd.Start(); err != nil {
	log.Fatal("Buffer Error:", err)
}

for {
	str, err := rd.ReadString('\n')
	if err != nil {
		log.Fatal("Read Error:", err)
		return
	}
	fmt.Println(str)
}

If you're trying to read from the reader in a goroutine with nothing to stop the script, it will exit.

答案2

得分: 3

另一个选项是bufio.NewScanner

package main

import (
   "bufio"
   "os/exec"
)

func main() {
   cmd := exec.Command("go", "env")
   out, err := cmd.StdoutPipe()
   if err != nil {
      panic(err)
   }
   buf := bufio.NewScanner(out)
   cmd.Start()
   defer cmd.Wait()
   for buf.Scan() {
      println(buf.Text())
   }
}

https://golang.org/pkg/bufio#NewScanner

英文:

Another option is bufio.NewScanner:

package main

import (
   "bufio"
   "os/exec"
)

func main() {
   cmd := exec.Command("go", "env")
   out, err := cmd.StdoutPipe()
   if err != nil {
      panic(err)
   }
   buf := bufio.NewScanner(out)
   cmd.Start()
   defer cmd.Wait()
   for buf.Scan() {
      println(buf.Text())
   }
}

https://golang.org/pkg/bufio#NewScanner

huangapple
  • 本文由 发表于 2012年10月25日 16:55:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/13065010.html
匿名

发表评论

匿名网友

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

确定