小编典典

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

go

下面的代码完全符合我的要求,只将其打印到控制台。

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

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

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

randomcommand.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

我想要实现的是:

  1. 实时将命令的stdout和stderr打印到控制台;
  2. 将命令的stdout和stderr记录到文件中,该文件与控制台中显示的完全相同。
  3. 尊重写到stdout和stderr的确切顺序,并且;
  4. 不修改命令本身

我试过将stdout和stderr命令传递到扫描仪中或使用io.Copy,它们分别在goroutine中进行,但是未维护输出顺序。可能是因为goroutine之间的交替必须在输出交替的同时发生,这几乎是不可能的。

我也尝试过使用“中选择”作为从例子在这里。但是没有保持输出顺序。

Logstreamer也存在同样的问题。

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

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


阅读 450

收藏
2020-07-02

共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实例包含一个日志文件和标准输出。

2020-07-02