go: os: add ProcessState.ExitCode

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”.

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 11
  • Comments: 17 (15 by maintainers)

Most upvoted comments

For anyone who finds this in a search like I did, this capability was implemented as the following in Go 1.12: https://golang.org/pkg/os/#ProcessState.ExitCode . As described in the first comment, this removes the need to cast the error twice, and the dependency on syscall.

I would not be at all surprised if we end up with users confused by unexpected -1 return-values.

At least they’ll see -1 and know they did something wrong, as opposed to returning 0 in that case.

Let’s just proceed with the simple signature approved during the proposal review.