上面第三种方式,我们直接是通过打开文件,然后将读取到的程序输出写入文件,但是实际上可能别人又已经封装好了一个logger,让你往logger里面写,比如,我这里就使用log包提供的log然后将程序的执行结果写入,但是因为使用了log包,所以写入的格式和log本身的格式造成格式会有部分的错乱,参考我的解决办法,解释都在注释里,如下:

package main

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

//通过管道同步获取日志的函数
func syncLog(logger *log.Logger, reader io.ReadCloser) {
	//因为logger的print方法会自动添加一个换行,所以我们需要一个cache暂存不满一行的log
	cache := ""
	buf := make([]byte, 1024, 1024)
	for {
		strNum, err := reader.Read(buf)
		if strNum > 0 {
			outputByte := buf[:strNum]
			//这里的切分是为了将整行的log提取出来,然后将不满整行和下次一同打印
			outputSlice := strings.Split(string(outputByte), "n")
			logText := strings.Join(outputSlice[:len(outputSlice)-1], "n")
			logger.Printf("%s%s", cache, logText)
			cache = outputSlice[len(outputSlice)-1]
		}
		if err != nil {
			if err == io.EOF || strings.Contains(err.Error(), "file already closed") {
				err = nil
			}
		}
	}
}

func main() {
	//定义一个每秒1次输出的shell
	cmdStr := `
#!/bin/bash
for var in {1..10}
do
	sleep 1
     echo "Hello, Welcome ${var} times "
done`
	cmd := exec.Command("bash", "-c", cmdStr)
	//这里得到标准输出和标准错误输出的两个管道,此处获取了错误处理
	cmdStdoutPipe, _ := cmd.StdoutPipe()
	cmdStderrPipe, _ := cmd.StderrPipe()
	err := cmd.Start()
	if err != nil {
		fmt.Println(err)
	}
	//打开一个文件,用作log封装输出
	f, _ := os.OpenFile("file.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	defer f.Close()
	//创建封装的log,第三个参数设置log输出的格式
	logger := log.New(f, "", log.LstdFlags)
	logger.Print("start print log:")
	oldFlags := logger.Flags()
	//为了保证shell的输出和标准的log格式不冲突,并且为了整齐,关闭logger自身的格式
	logger.SetFlags(0)
	go syncLog(logger, cmdStdoutPipe)
	go syncLog(logger, cmdStderrPipe)
	err = cmd.Wait()
	//执行完后再打开log输出的格式
	logger.SetFlags(oldFlags)
	logger.Print("log print done")
	if err != nil {
		fmt.Println(err)
	}
}

程序执行结果如下:

可以看到,shell的执行过程中的log是没有日志的前缀的,这样也保证了log的整齐:)