Golang逐行输出stdout时遇到问题如何解决

Golang逐行输出stdout时遇到问题如何解决 当我尝试从标准输出正常读取时,一切工作正常,但当我逐行读取时,输出会挂起并以退出码2退出。我使用了StdoutPipe,并且了解到管道有默认的65536字节限制,所以这可能是中断的原因吗?数据流非常快,逐行读取时可能来不及读取,导致管道溢出?我也尝试过将标准输出复制到缓冲区,但同样的情况发生了,程序挂起然后被终止。在这种情况下通常应该怎么做,你知道吗?

cmd := exec.Command("tshark", "-i", d.device, "-T", "json")
stdoutReader, err := cmd.StdoutPipe()
if err != nil {
  log.Println(err)
}
go func() {
  var b bytes.Buffer
  if _, err := io.Copy(&b, stdoutReader); err != nil {
    log.Println(err)
 }
}()
scanner := bufio.NewScanner(stdoutReader)
done := make(chan bool)
go func() {
  for scanner.Scan() {
    fmt.Println(scanner.Bytes())
  }
  done <- true
}()
if err := cmd.Start(); err != nil {
  log.Println(err)
}
<-done
if err = cmd.Wait(); err != nil {
  log.Println(err)
}

更多关于Golang逐行输出stdout时遇到问题如何解决的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

通过使用 bufio.NewReader(stdout) 解决了问题,复制时不需要缓冲区,一切正常,并且没有因管道溢出而导致的中断。

更多关于Golang逐行输出stdout时遇到问题如何解决的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你的代码中存在一个关键问题:同一个 stdoutReader 被多个 goroutine 同时读取io.Copybufio.NewScanner 都在尝试从同一个管道读取数据,这会导致数据竞争和不可预测的行为,包括管道阻塞和程序挂起。

管道是单向数据流,数据一旦被一个读取器消费,另一个读取器就无法获取。在你的代码中,io.Copyscanner 会互相干扰,导致部分数据丢失,进而可能触发管道缓冲区满或读取超时,最终进程被终止。

解决方案是只使用一个读取器。根据你的需求选择以下两种方式之一:

方案一:使用 bufio.Scanner 逐行读取(推荐)

如果你需要逐行处理输出,直接使用 scanner 即可,移除 io.Copy 的部分:

cmd := exec.Command("tshark", "-i", d.device, "-T", "json")
stdoutReader, err := cmd.StdoutPipe()
if err != nil {
    log.Fatal(err)
}

scanner := bufio.NewScanner(stdoutReader)
if err := cmd.Start(); err != nil {
    log.Fatal(err)
}

// 可选:设置缓冲区大小以避免长行溢出
const maxCapacity = 1024 * 1024 // 1MB
buf := make([]byte, maxCapacity)
scanner.Buffer(buf, maxCapacity)

for scanner.Scan() {
    fmt.Println(scanner.Text()) // 或 scanner.Bytes()
}

if err := scanner.Err(); err != nil {
    log.Println("读取错误:", err)
}

if err := cmd.Wait(); err != nil {
    log.Println("命令执行错误:", err)
}

方案二:使用 io.Copy 到缓冲区,然后处理

如果你需要先将所有输出保存到缓冲区,再逐行处理:

cmd := exec.Command("tshark", "-i", d.device, "-T", "json")
stdoutReader, err := cmd.StdoutPipe()
if err != nil {
    log.Fatal(err)
}

var b bytes.Buffer
if err := cmd.Start(); err != nil {
    log.Fatal(err)
}

// 将输出复制到缓冲区
go func() {
    if _, err := io.Copy(&b, stdoutReader); err != nil {
        log.Println("复制错误:", err)
    }
}()

if err := cmd.Wait(); err != nil {
    log.Println("命令执行错误:", err)
}

// 从缓冲区逐行读取
scanner := bufio.NewScanner(&b)
for scanner.Scan() {
    fmt.Println(scanner.Text())
}

if err := scanner.Err(); err != nil {
    log.Println("扫描错误:", err)
}

关键点说明:

  1. 不要重复读取同一个管道:每个管道只能有一个消费者。
  2. 缓冲区大小:如果 tshark 输出的行非常长,可能需要通过 scanner.Buffer() 增加缓冲区大小,避免 bufio.ErrTooLong 错误。
  3. 错误处理:始终检查 scanner.Err()cmd.Wait() 的返回错误。
  4. goroutine 同步:在方案二中,io.Copy 需要在单独的 goroutine 中执行,因为 cmd.Wait() 会等待命令结束并关闭管道。

根据你的实际需求选择方案一(实时逐行处理)或方案二(先保存后处理)。方案一更节省内存,适合流式处理;方案二适合需要完整输出后再分析的场景。

回到顶部