简体   繁体   English

如何理解 exec.cmd 是否被取消

[英]How to understand if exec.cmd was canceled

I'm trying to return specific error when the command was canceled by context.当命令被上下文取消时,我试图返回特定错误。 After investigating ProcessState understood that if got -1 in exitCode the process got terminate signal https://golang.org/pkg/os/#ProcessState.ExitCode but maybe we have more elegant way?在调查 ProcessState 后了解到,如果在 exitCode 中得到 -1,则进程得到终止信号https://golang.org/pkg/os/#ProcessState.ExitCode但也许我们有更优雅的方法? Maybe I can put this error from cancel function?也许我可以将这个错误从取消功能中删除? Maybe it isn't good enough exitCode for understanding if the command was canceled?也许 exitCode 不足以理解命令是否被取消?

var (
    CmdParamsErr = errors.New("failed to get params for execution command")
    ExecutionCanceled = errors.New("command canceled")
)

func execute(m My) error {
    filePath, args, err := cmdParams(m)
    err = nil
    if err != nil {
        log.Infof("cmdParams: err: %v\n, m: %v\n", err, m)
        return CmdParamsErr
    }

    var out bytes.Buffer
    var errStd bytes.Buffer
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    defer cancel()
    cmd := exec.CommandContext(ctx, filePath, args...)
    cmd.Stdout = &out
    cmd.Stderr = &errStd
    err = cmd.Run()
    if err != nil {
        if cmd.ProcessState.ExitCode() == -1 {
            log.Warnf("execution was canceled by signal, err: %v\n", err)
            err = ExecutionCanceled
            return err
        } else {
            log.Errorf("run failed, err: %v, filePath: %v, args: %v\n", err, filePath, args)
            return err
        }
    }
    return err
}

exec.ExitError doesn't provide any reason for the exit code (there is no relevant struct field nor an Unwrap method), so you have to check the context directly: exec.ExitError不提供任何退出代码的原因(没有相关的结构字段,也没有 Unwrap 方法),因此您必须直接检查上下文:

if ctx.Err() != nil {
    log.Println("canceled")
}   

Note that this is a slight race because the context may be canceled just after the command failed for a different reason, but there is nothing you can do about that.请注意,这是一个轻微的竞争,因为上下文可能会在命令因其他原因失败后立即取消,但您对此无能为力。

There is no straightforward or elegant way to figure out if a process was killed because a context was canceled.没有直接或优雅的方法来确定进程是否因为上下文被取消而被终止。 The closest you can come is this:最接近你的是:

func run() error {
    ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
    defer cancel()

    cmd := exec.CommandContext(ctx, "bash", "-c", "exit 1")

    // Start() returns an error if the process can't be started. It will return
    // ctx.Err() if the context is expired before starting the process.

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

    if err := cmd.Wait(); err != nil {
        if e, ok := err.(*exec.ExitError); ok {

            // If the process exited by itself, just return the error to the
            // caller.

            if e.Exited() {
                return e
            }

            // We know now that the process could be started, but didn't exit
            // by itself. Something must have killed it. If the context is done,
            // we can *assume* that it has been killed by the exec.Command.
            // Let's return ctx.Err() so our user knows that this *might* be
            // the case.

            select {
            case <-ctx.Done():
                return ctx.Err()
            default:
                return e
            }
        }

        return err
    }

    return nil
}

The problem here is that there might be a race condition, so returning ctx.Err() might be misleading.这里的问题是可能存在竞争条件,因此返回ctx.Err()可能会产生误导。 For example, imagine the following scenario:例如,想象以下场景:

  1. The process starts.该过程开始。
  2. The process is killed by an external actor.该进程被外部参与者杀死。
  3. The context is canceled.上下文被取消。
  4. You check the context.你检查上下文。

At this point, the function above would return ctx.Err() , but this might be misleading because the reason why the process was killed is not because the context was canceled.此时,上面的函数将返回ctx.Err() ,但这可能会产生误导,因为进程被ctx.Err()的原因不是因为上下文被取消。 If you decide to use a code similar to the function above, keep in mind this approximation.如果您决定使用类似于上述函数的代码,请记住这个近似值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM