【问题标题】:Scanning both stdout and stderr simultaneously同时扫描 stdout 和 stderr
【发布时间】:2017-10-02 00:04:01
【问题描述】:

我正在寻找一种将 stdout 和 stderr 作为一个流同时处理的方法。 对于标准输出,我可以使用:

cmd := exec.Command(command, flags...)
var wg sync.WaitGroup

stdout, err := cmd.StdoutPipe()
if err != nil {
    return fmt.Errorf("RunCommand: cmd.StdoutPipe(): %v", err)
}

if err := cmd.Start(); err != nil {
    return fmt.Errorf("RunCommand: cmd.Start(): %v", err)
}

scanner := bufio.NewScanner(stdout)
scanner.Split(ScanLinesR)
wg.Add(1)
go func() {
    for scanner.Scan() {
        text := scanner.Text()
        if strings.TrimSpace(text) != "" {
            DoWhateverYouNeedWithText(text)
        }
    }
    wg.Done()
}()

wg.Wait()

但是如何将 stderr 添加到相同的代码中?

【问题讨论】:

  • 复制stderr的stdout代码(创建管道,添加一个到等待组,启动goroutine)。
  • 如果您需要串行处理这些行,请复制 stderr 的 stdout 代码,但不要在这些 goroutine 中的每一行上调用您的函数,而是将每一行写入一个通道并使用第三个 goroutine 读取通道并处理线路。

标签: go exec stdout stderr


【解决方案1】:

正如上述评论者建议的那样,我添加了 2 个 goroutine(一个用于 stderr,一个用于关闭通道)。

var wg sync.WaitGroup

stdout, err := cmd.StdoutPipe()
if err != nil {
    return fmt.Errorf("RunCommand: cmd.StdoutPipe(): %v", err)
}
stderr, err := cmd.StderrPipe()
if err != nil {
    return fmt.Errorf("RunCommand: cmd.StderrPipe(): %v", err)
}

if err := cmd.Start(); err != nil {
    return fmt.Errorf("RunCommand: cmd.Start(): %v", err)
}

outch := make(chan string, 10)

scannerStdout := bufio.NewScanner(stdout)
scannerStdout.Split(ScanLinesR)
wg.Add(1)
go func() {
    for scannerStdout.Scan() {
        text := scannerStdout.Text()
        if strings.TrimSpace(text) != "" {
            outch <- text
        }
    }
    wg.Done()
}()
scannerStderr := bufio.NewScanner(stderr)
scannerStderr.Split(ScanLinesR)
wg.Add(1)
go func() {
    for scannerStderr.Scan() {
        text := scannerStderr.Text()
        if strings.TrimSpace(text) != "" {
            outch <- text
        }
    }
    wg.Done()
}()

go func() {
    wg.Wait()
    close(outch)
}()

for t := range outch {
    DoWhateverYouNeedWithText(t)
}

【讨论】:

    【解决方案2】:
    // child.go
    
    func main() {
        fmt.Fprintln(os.Stdout, "Child started.")
        time.Sleep(time.Second * 1)
        fmt.Println("Tick...")
        time.Sleep(time.Second * 1)
        fmt.Println("Tick...")
        time.Sleep(time.Second * 1)
        fmt.Println("Tick...")
        time.Sleep(time.Second * 2)
        fmt.Println("Child ended.")
    }
    
    // childerr.go
    func main() {
        fmt.Fprintln(os.Stdout, "Child started.")
        time.Sleep(time.Second * 1)
        fmt.Fprintln(os.Stdout, "Tick...")
        time.Sleep(time.Second * 1)
        fmt.Fprintln(os.Stdout, "Tick...")
        time.Sleep(time.Second * 1)
        fmt.Fprintln(os.Stdout, "Tick...")
        time.Sleep(time.Second * 2)
        panic("testing")
        fmt.Fprintln(os.Stdout, "Child ended.")
    }
    
    func executeCommand(output chan<- string, err chan<- error, start chan interface{}, cmd *exec.Cmd) {
    
        defer close(start)
    
        stdout, e := cmd.StdoutPipe()
        if e != nil {
            log.Println("Error starting Cmd: ", e)
            err <- e
            return
        }
    
        cmd.Stderr = cmd.Stdout
    
        <-start
    
        scanner := bufio.NewScanner(stdout)
    
        for scanner.Scan() {
            output <- scanner.Text()
        }
    
    }
    
    func run(cmd *exec.Cmd) {
    
        start := make(chan interface{})
        output := make(chan string)
        errChan := make(chan error)
        defer close(output)
        defer close(errChan)
    
        go executeCommand(output, errChan, start, cmd)
    
        start <- nil
    
        if e := cmd.Start(); e != nil {
            log.Println("Error starting Cmd: ", e)
        }
    
        for {
            select {
            case b := <-output:
                fmt.Println(b)
            case e := <-errChan:
                fmt.Println("ERR EXECUTING COMMAND->", e)
                return
            case <-start:
                fmt.Println("COMPLETED")
                return
            }
        }
    }
    

    使用 child 或 childerr 创建示例长时间运行的过程。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-04-23
      • 2020-01-11
      • 1970-01-01
      • 2019-08-21
      • 1970-01-01
      • 2020-05-30
      • 1970-01-01
      相关资源
      最近更新 更多