如何在实时输出的同时将命令的 stdout 和 stderr 重定向到控制台和日志文件?

Posted

技术标签:

【中文标题】如何在实时输出的同时将命令的 stdout 和 stderr 重定向到控制台和日志文件?【英文标题】:How can I redirect the stdout and stderr of a command to both the console and a log file while outputting in real time? 【发布时间】:2015-06-23 04:11:16 【问题描述】:

以下代码完全符合我的要求,只是它只打印到控制台。

cmd := exec.Command("php", "randomcommand.php")

cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

if err := cmd.Run(); err != nil 
    log.Fatal(err)

随机命令.php:

// randomcommand.php simply alternates output between stdout and stderr 20 times

$stdout = fopen('php://stdout', 'w+');
$stderr = fopen('php://stderr', 'w+');
for ($i = 1;$i <= 20; $i++) 
    fwrite($stdout, "stdout $i\n");
    fwrite($stderr, "stderr $i\n");

输出:

stdout 1
stderr 1
stdout 2
stderr 2
stdout 3
stderr 3
stdout 4
stderr 4
stdout 5
stderr 5
stdout 6
stderr 6
stdout 7
stderr 7
stdout 8
stderr 8
stdout 9
stderr 9
stdout 10
stderr 10
stdout 11
stderr 11
stdout 12
stderr 12
stdout 13
stderr 13
stdout 14
stderr 14
stdout 15
stderr 15
stdout 16
stderr 16
stdout 17
stderr 17
stdout 18
stderr 18
stdout 19
stderr 19
stdout 20
stderr 20

我想要实现的是:

    将命令的stdout和stderr实时打印到控制台; 将命令的 stdout 和 stderr 记录到文件中,与控制台中显示的完全相同; 遵守写入 stdout 和 stderr 的确切顺序,并且; 不修改命令本身

我已经尝试将命令 stdout 和 stderr 传送到扫描仪或使用 io.Copy,每个都在一个 goroutine 中,但输出的顺序没有得到维护。可能是因为 goroutine 之间的交替必须与输出交替同时发生,这几乎是不可能的。

我还尝试了使用“选择”,如 here 中的示例。但是输出顺序没有保持。

Logstreamer 也有同样的问题。

我必须尝试的另一个想法是看看我是否可以以某种方式从控制台缓冲区中读取,但这感觉不对。

有谁知道这是否可能并且值得追求?

【问题讨论】:

为什么不只是外壳。 php xxx.php &amp;&gt;file Streaming commands output progress 的可能副本 你知道你的四点因为缓冲而达不到吗? golang.org/pkg/io/#MultiWriter 查看 MultiWriter。它允许定义多个目标(例如标准输出和文件;)) 【参考方案1】:

正如我在评论部分所说,这可以使用MultiWriter来实现

package main

import (
    "io"
    "log"
    "os"
    "os/exec"
)

func main() 
    // Logging capability
    f, err := os.OpenFile("log.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
    if err != nil 
        log.Fatalf("Error opening file: %v", err)
    
    defer f.Close()
    mwriter := io.MultiWriter(f, os.Stdout)
    cmd := exec.Command("ls")
    cmd.Stderr = mwriter
    cmd.Stdout = mwriter
    err = cmd.Run() //blocks until sub process is complete
    if err != nil 
        panic(err)
    

当你声明你的命令时,在你运行它之前,只需指定 Stdout 和 Stderr 使用上面定义的 MultiWriter。这个 MultiWriter 实例包含一个日志文件和标准输出。

【讨论】:

Depado,你疯了!答案是如此简单,以至于我无法在我面前看到它。谢谢!

以上是关于如何在实时输出的同时将命令的 stdout 和 stderr 重定向到控制台和日志文件?的主要内容,如果未能解决你的问题,请参考以下文章

如何从命令中获取输出以实时显示在窗体上的控件中?

如何将实时stdout流拆分成几个文件?

R:同时重定向到标准输出和动态文件

使用系统命令将stdout和stderr输出重定向到文件在perl中不起作用[重复]

在 Bash 中同时捕获标准输出和标准错误 [重复]

管道输出subprocess.Popen到文件