在Go中,如何在输出实时的同时将命令的stdout和stderr重定向到控制台和日志文件? [英] How can I redirect, in Go, the stdout and stderr of a command to both the console and a log file while outputting in real time?

查看:634
本文介绍了在Go中,如何在输出实时的同时将命令的stdout和stderr重定向到控制台和日志文件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  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简单地在stdout和stderr之间交替输出20次

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

输出:

  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记录到文件中,与控制台中显示的一样; / li>
  3. 尊重写入stdout和stderr的确切顺序,以及;

  4. 不修改命令本身

我试过将命令stdout和stderr输入扫描器或使用io 。复制,每个都在goroutine中,但输出的顺序不被保留。可能是因为goroutine之间的交替必须在输出交替的同时发生,这几乎是不可能的。

我也尝试使用select,如 here 。但输出的顺序未被维护。



Logstreamer相同的问题



我必须尝试的另一个想法是查看我是否可以通过控制台缓冲区读取数据,但这样做感觉不对。



有谁知道这是否甚至是可能的并值得追求?

解决方案

正如我在评论部分所说的,这可以通过 MultiWriter

完成。

 包裹主要

进口(
io
记录
os
os / exec


func main(){
//记录能力
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)
}
推迟f.Close()
mwriter:= io.MultiWriter(f,os.Std out)
cmd:= exec.Command(ls)
cmd.Stderr = mwriter
cmd.Stdout = mwriter
err = cmd.Run()//直到如果err!= nil {
panic(err)
}
}

子过程完成


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

The following bit of code does exactly what I want, except it only prints to the console.

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");
}

Output:

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

What I'm trying to achieve is:

  1. Print the command's stdout and stderr to the console in real-time;
  2. Log the command's stdout and stderr to a file, exactly as it appears in the console;
  3. Respect the exact order of writes to stdout and stderr, and;
  4. Not modify the command itself

I've tried piping the command stdout and stderr into scanners or using io.Copy, each in a goroutine, but order of the output was not maintained. Probably because alternating between goroutines would have to occur at the same time as output alternates, which is just about impossible.

I also tried the using "select" as in the examples from here. But order of output was not maintained.

Same problem with Logstreamer.

The other idea I have to try is to see if I can somehow just read from the console buffer, but this just doesn't feel right.

Does anyone know if this is even possible and worth pursuing?

解决方案

As I said in the comment section, this can be achieved using 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)
    }
}

When you declare your command, and before you run it, just specify that Stdout and Stderr are using the MultiWriter defined above. This MultiWriter instance contains both a log file and the standard output.

这篇关于在Go中,如何在输出实时的同时将命令的stdout和stderr重定向到控制台和日志文件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆