我想将
os/exec
命令的输出写入多个文件。从我在网上发现的情况来看,io.MultiWriter 似乎是完成这项工作的正确工具。我测试了它(请参阅下面的示例代码),它确实将输出写入多个文件。
multiWriter := io.MultiWriter(logFile1, logFile2)
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
cmd := exec.CommandContext(ctx, "bash", "-c", "command")
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
cmd.Stdout = multiWriter
cmd.Stderr = multiWriter
cmd.Start()
cmd.Wait()
我遇到的问题是,如果存在超时(由
ctx
定义),cmd.Wait() 命令不会按预期中断。根据调试器的说法,它挂起,等待来自 https://go.dev/src/os/exec/exec.go. 第 957 行
return <-c.goroutineErr
*os.File
变量直接设置为 cmd.Stdout
和 cmd.Stderr
时,c.goroutineErr
chan 为零,并且对 cmd.Wait()
的调用按预期中断。
来自 os/exec 文档:
// Stdout and Stderr specify the process's standard output and error.
//
// If either is nil, Run connects the corresponding file descriptor
// to the null device (os.DevNull).
//
// If either is an *os.File, the corresponding output from the process
// is connected directly to that file.
//
// Otherwise, during the execution of the command a separate goroutine
// reads from the process over a pipe and delivers that data to the
// corresponding Writer. In this case, Wait does not complete until the
// goroutine reaches EOF or encounters an error or a nonzero WaitDelay
// expires.
//
// If Stdout and Stderr are the same writer, and have a type that can
// be compared with ==, at most one goroutine at a time will call Write.
向命令添加 WaitDelay 的正确方法是吗?
难道你的程序正在抵抗被杀死吗?它是以
root
或其他方式运行吗?
以下代码对我来说效果很好(在 Linux 上)。两个文件:
#!/bin/bash
# Call this "drip.sh"
let x=0
while true; do
let x=${x}+1
echo "drip ${x}"
sleep 1
done
和一些 Go 代码:
// Program timeout (timeout.go).
package main
import (
"bytes"
"context"
"fmt"
"io"
"os/exec"
"syscall"
"time"
)
func main() {
timeout := 5 * time.Second
ctx := context.Background()
var logFile1, logFile2 bytes.Buffer
multiWriter := io.MultiWriter(&logFile1, &logFile2)
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
cmd := exec.CommandContext(ctx, "bash", "-c", "./drip.sh")
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
cmd.Stdout = multiWriter
cmd.Stderr = multiWriter
cmd.Start()
cmd.Wait()
fmt.Println("program exited")
fmt.Printf("log1: %q\n", logFile1.String())
fmt.Printf("log2: %q\n", logFile2.String())
}
运行它(等待编译和程序超时):
$ go run timeout.go
program exited
log1: "drip 1\ndrip 2\ndrip 3\ndrip 4\ndrip 5\n"
log2: "drip 1\ndrip 2\ndrip 3\ndrip 4\ndrip 5\n"
根据系统负载,您可能会得到 4,5 或 6 行输出。