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
通过使用 bufio.NewReader(stdout) 解决了问题,复制时不需要缓冲区,一切正常,并且没有因管道溢出而导致的中断。
更多关于Golang逐行输出stdout时遇到问题如何解决的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
你的代码中存在一个关键问题:同一个 stdoutReader 被多个 goroutine 同时读取。io.Copy 和 bufio.NewScanner 都在尝试从同一个管道读取数据,这会导致数据竞争和不可预测的行为,包括管道阻塞和程序挂起。
管道是单向数据流,数据一旦被一个读取器消费,另一个读取器就无法获取。在你的代码中,io.Copy 和 scanner 会互相干扰,导致部分数据丢失,进而可能触发管道缓冲区满或读取超时,最终进程被终止。
解决方案是只使用一个读取器。根据你的需求选择以下两种方式之一:
方案一:使用 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)
}
关键点说明:
- 不要重复读取同一个管道:每个管道只能有一个消费者。
- 缓冲区大小:如果
tshark输出的行非常长,可能需要通过scanner.Buffer()增加缓冲区大小,避免bufio.ErrTooLong错误。 - 错误处理:始终检查
scanner.Err()和cmd.Wait()的返回错误。 - goroutine 同步:在方案二中,
io.Copy需要在单独的 goroutine 中执行,因为cmd.Wait()会等待命令结束并关闭管道。
根据你的实际需求选择方案一(实时逐行处理)或方案二(先保存后处理)。方案一更节省内存,适合流式处理;方案二适合需要完整输出后再分析的场景。

