Skip to content

os: add ProcessState.ExitCode #26539

@benhoyt

Description

@benhoyt

After a bit of discussion on this golang-nuts thread, I propose that we add an ExitStatus() int method to os.ProcessState so that it's significantly simpler to get the exit status of a finished process, and doesn't require the syscall package.

Motivation (some of this copied from the thread): I struggled to get the exit status integer of a command executed with os/exec. I followed the documentation through ExitError and ProcessState, but could only find the ProcessState.Success() boolean. After searching Google+StackOverflow I found you can get it, but it requires importing syscall and converting to a syscall type -- pretty klunky.

Here's roughly the code snippet that I'm currently using:

err = cmd.Wait()
if err != nil {
    if exitErr, ok := err.(*exec.ExitError); ok {
        if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
            return status.ExitStatus()
        }
    }
    return -1
}
return 0

Which is problematic because of all the boilerplate, but also because syscall is platform-specific, and fetching an exit code works on all major platforms (Unix/Linux/macOS/Windows). Even on Plan 9 ExitStatus() is implemented (though admittedly it's a bit of a hack as it just checks to see whether an error message was returned). So this would work for all major systems, and on Plan 9 would just act like the above syscall function that already exists, returning 0 on success or 1 on error.

os.ProcessState already has a Success() bool method, so this proposal would add ExitStatus() int alongside that. It would return the exit status integer (and possibly be documented to return -1 if the process hasn't finished). This would enable the above code to be something like this:

err = cmd.Wait()
if err != nil {
    if exitErr, ok := err.(*exec.ExitError); ok {
        return exitErr.ExitStatus() // ExitError embeds ProcessState
    }
    return -1
}
return 0

Or actually just this:

_ = cmd.Wait()
return cmd.ProcessState.ExitStatus()

There are various reasons for needing the exit code value:

  • To log it in a structured way
  • To switch on the exit code when running a utility that returns different well-defined codes for different kinds of errors that you need to detect
  • To display it in (say) a bold font in a UI control
  • To return it from a system() function when implementing a scripting language (my particular case)

This exists in other languages, for example Python's subprocess has "returncode" and Java's exec has exitValue().

For what it's worth, @ianlancetaylor said (on the linked golang-nuts thread) that this "seems reasonable to me".

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions