我的功能
func RunCommand(cmdPath string, cmdArgs string) (error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
cmd := exec.CommandContext(ctx, cmdPath, cmdArgs)
stdout, err := cmd.StdoutPipe()
if err != nil {
return err
}
defer stdout.Close()
scanner := bufio.NewScanner(stdout)
err := cmd.Start()
if err != nil {
return err
}
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
for scanner.Scan() {
doSomething(scanner.Text())
}
if scanner.Err() != nil {
log.Errf("Error reading stdout, %v", scanner.Err())
}
}()
done := make(chan error, 1)
go func() {
done <- cmd.Wait()
}()
select {
case <-ctx.Done():
return ctx.Err()
case err := <-done:
if err != nil {
return err
}
}
wg.Wait()
return nil
}
我只能在出现此错误时重现此问题几次:读取标准输出时出错,读取 |0:文件已关闭。我的理解是我在检查 Scanner.Err() 之前关闭标准输出,但为什么会发生这种情况。我在 go 例程中使用 cmd.Wait() ,它应该等待 Scanner.Err() 然后关闭标准输出,对吗?
该错误很可能是由子进程意外终止引起的。
考虑这个例子。 我们每四分之一秒开始一次 bash 循环打印。 但一秒钟后,一个 goroutine 通过 pid 杀死了该进程。 其输出如下。
package main
import (
"bufio"
"context"
"log"
"os"
"os/exec"
"sync"
"syscall"
"time"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
cmd := exec.CommandContext(ctx, "sh", "-c", "for i in $(seq 1 10); do echo $i; sleep 0.25; done")
cmd.Stderr = os.Stderr
stdout, err := cmd.StdoutPipe()
if err != nil {
log.Fatal(err)
}
defer stdout.Close()
scanner := bufio.NewScanner(stdout)
if err := cmd.Start(); err != nil {
log.Fatal(err)
}
go func() { time.Sleep(1 * time.Second); syscall.Kill(cmd.Process.Pid, syscall.SIGKILL) }()
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
for scanner.Scan() {
log.Print(scanner.Text())
}
if scanner.Err() != nil {
log.Printf("Error reading stdout, %v", scanner.Err())
}
}()
done := make(chan error, 1)
go func() {
done <- cmd.Wait()
}()
select {
case <-ctx.Done():
log.Fatal(ctx.Err())
case err := <-done:
if err != nil {
log.Fatal(err)
}
}
wg.Wait()
}
2024/09/18 22:34:44 1
2024/09/18 22:34:45 2
2024/09/18 22:34:45 3
2024/09/18 22:34:45 4
2024/09/18 22:34:45 Error reading stdout, read |0: file already closed
2024/09/18 22:34:45 signal: killed
exit status 1