上面第三种方式,我们直接是通过打开文件,然后将读取到的程序输出写入文件,但是实际上可能别人又已经封装好了一个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的整齐:)