Golang调用linux命令

近期有用Golang调用外部命令的需求,期间也踩了些坑。这里整理了一下代码发出来。目前开发用的操作系统是Ubuntu 22,Go的版本是1.18.3。

type Result struct {
	Code   int
	StdOut []byte
	ErrOut []byte
}

func ExecuteCmd(dur time.Duration, args ...string) (result *Result, err error) {

	ctx, cancel := context.WithTimeout(context.Background(), dur)
	defer cancel()

	if len(args) == 0 {
		err = errors.New("invalid parameters, command is empty")
		return
	}

	var stdout, stderr bytes.Buffer
	cmd := exec.Command("/bin/bash", "-c", strings.Join(args, " "))
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}

	if err = cmd.Start(); err != nil {
		return
	}

	notify := make(chan struct{})
	go func() {
		err = cmd.Wait()
		close(notify)
	}()

	select {
	case <-notify:
	case <-ctx.Done():
		if err = syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL); err == nil {
			err = errors.New("execution timeout")
		}
		// return directly when execution timeout
		return
	}

	result = new(Result)
	result.Code = cmd.ProcessState.ExitCode()
	if stdout.Len() > 0 {
		result.StdOut = stdout.Bytes()
	}
	if stderr.Len() > 0 {
		result.ErrOut = stderr.Bytes()
	}

	return
}

1、golang本来有一个exec.CommandContext函数用于传入带超时控制的context,用于实现超时控制。但这个函数对超时控制不是很好,有可能会挂起。同时也发生过进程无法删除,成为僵尸进程。

2、SysProcAttr的Setpgid设置为true,主要是为了超时杀掉主进程时可以将命令行进程一起杀掉,避免僵尸进程。

3、cmd.Wait()调用放在go协程中主要是为了避免被调用的命令行挂起时杀进程异常。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值