工具类 

package cmd

import (
	"bufio"
	"logger"
	"fmt"
	"io"
	"os/exec"
	"sync"
	"syscall"
)

type Command struct {
	CmdStr          string
	Pid             int
	ExitCode        int
	StdOutput       string
	ErrOutput       string
	isPrintRealTime bool
}

var log = logger.GetLogger()

const (
	ExitCodeDefault    = -999
	ExitCodeIoError    = -998
	ExitCodeStartError = -997
)

/**
创建命令执行实例
*/
func NewCmd(cmdStr string) *Command {
	return &Command{
		CmdStr:          cmdStr,
		ExitCode:        ExitCodeDefault,
		isPrintRealTime: false,
	}
}

/**
创建命令执行实例,并且实时打印输出
*/
func NewCmdWithPrint(cmdStr string) *Command {
	return &Command{
		CmdStr:          cmdStr,
		ExitCode:        ExitCodeDefault,
		isPrintRealTime: true,
	}
}

/**
执行命令
*/
func (cmd *Command) Start() {
	wg := &sync.WaitGroup{}
	wg.Add(2)
	cmdExec := exec.Command("sh", "-c", cmd.CmdStr)
	cmdExec.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} // 将PGID设置成与PID相同的值 保证kill能够杀死孙子进程
	stdout, err := cmdExec.StdoutPipe()
	if err != nil {
		log.Error(fmt.Sprintf("get stdout failed: %s", err.Error()))
		cmd.ExitCode = ExitCodeIoError
		cmd.ErrOutput += err.Error()
		return
	}
	errout, err := cmdExec.StderrPipe()
	if err != nil {
		log.Error(fmt.Sprintf("get stdout failed: %s", err.Error()))
		cmd.ExitCode = ExitCodeIoError
		cmd.ErrOutput += err.Error()
		return
	}

	// 异步读取输出流
	go func() {
		defer wg.Done()
		cmd.readInputStream(stdout, false)
	}()
	go func() {
		defer wg.Done()
		cmd.readInputStream(errout, true)
	}()

	log.Info(fmt.Sprintf("start process %s", cmd.CmdStr))
	err = cmdExec.Start()
	if err != nil {
		log.Error(fmt.Sprintf("start process failed: %s", err.Error()))
		cmd.ExitCode = ExitCodeStartError
		cmd.ErrOutput += err.Error()
		return
	}
	cmd.Pid = cmdExec.Process.Pid
	wg.Wait()
	_ = cmdExec.Wait()
	cmd.ExitCode = cmdExec.ProcessState.ExitCode()
}

func (cmd *Command) Kill() error {
	log.Info(fmt.Sprintf("kill process %s,pid %d", cmd.CmdStr, cmd.Pid))
	return syscall.Kill(-cmd.Pid, syscall.SIGKILL)
}

/**
读取输出流,如果是错误输出流,默认实时打印
*/
func (cmd *Command) readInputStream(in io.ReadCloser, isError bool) {
	reader := bufio.NewReader(in)
	for {
		line, err := reader.ReadString('\n')
		if err != nil {
			if io.EOF != err {
				log.Error(err.Error())
			}
			break
		}
		if isError {
			cmd.ErrOutput += line
			log.Error(line)
		} else {
			cmd.StdOutput += line
			if cmd.isPrintRealTime {
				log.Info(line)
			}
		}
	}
}

使用方式

package main

import (
	"utils/cmd"
	"utils/logger"
	"fmt"
)

var log = logger.GetLogger()

func main() {
    cmdExec := cmd.NewCmd(cmdStr)
    cmdExec.Start()
    if cmdExec.ExitCode != 0 {
        log.Error(fmt.Sprintf(
            "start %s failed: %s exitcode: %d",
            cmdStr, cmdExec.ErrOutput, cmdExec.ExitCode))
    }
}