Sorry for bumping an oldish thread, but I just ran into this trying to
propagate an exit code.

I didn't find anything searching that was sufficiently
platform-independent, but a little spelunking into the stdlib source
bore fruit, so I thought I'd share my solution.

If you have an *exec.ExitError it embeds an *os.ProcessState.

*os.ProcessState's Sys method returns an interface{} containing the a
platform specific type from the syscall package.

But all the platform specific types have a method ExitStatus() int.
(Even nacl and Plan 9, which fake it).

So, given an *exec.ExitError x, to get the exit code you just need to do:

exitCode := x.Sys().(interface{
  ExitStatus() int
}).ExitStatus()

I suppose it wouldn't hurt to check that type assertion and return -1
if it fails, though I imagine if that ever happened it would be a bug
in a new port.


On Thu, Jun 2, 2016 at 4:14 AM, Konstantin Khomoutov
<flatw...@users.sourceforge.net> wrote:
> On Wed, 1 Jun 2016 23:56:43 +0300
> Janne Snabb <sn...@epipe.com> wrote:
>
>> You should check if the type of err is *exec.ExitError and in that
>> case consider it just as non-zero exit value from the process.
>>
>> The following snippet from
>> https://golang.org/src/os/exec/exec_test.go#L123 clarifies it:
>>
>> func TestExitStatus(t *testing.T) {
>>       // Test that exit values are returned correctly
>>       cmd := helperCommand(t, "exit", "42")
>>       err := cmd.Run()
>>       want := "exit status 42"
>>
>>       if werr, ok := err.(*exec.ExitError); ok {
>>               if s := werr.Error(); s != want {
>>                       t.Errorf("from exit 42 got exit %q, want %q",
>> s, want) }
>>       } else {
>>               t.Fatalf("expected *exec.ExitError from exit 42; got %
>> T: %v", err, err) }
>> }
>
> I'd warn the OP to take this snippet with a hefty grain of salt: while
> it's perfectly reasonable for the test suite of an stdlib package to
> test the output of os/exec.ExitError.Error(), 3rd-party code must not
> rely on the output of the Error() methods of error values of any type to
> be predictable and stable -- at least until there's absolutely no
> other way to get onto error's details.
>
> That is, the OP should consider writing a helper function to test the
> error returned by os/exec.Cmd.Run() et al to check whether the exit
> status is non-zero and discard the error value if so.
>
> An example:
>
> ----8<----
>     package main
>
>     import (
>         "os/exec"
>         "testing"
>     )
>
>     func maybeIgnore(err error) error {
>         if _, ok := err.(*exec.ExitError); ok {
>                 return nil
>         }
>         return err
>     }
>
>     func TestExitCode(t *testing.T) {
>         cmd := exec.Command("/bin/sh", "-c", "exit 42")
>         err := maybeIgnore(cmd.Run())
>         if err != nil {
>                 t.Error("Expected nil error, got: %#v", err)
>         }
>         cmd = exec.Command("./does not exist, really")
>         err = maybeIgnore(cmd.Run())
>         if err == nil {
>                 t.Error("Expected non-nil error, got nil")
>         }
>     }
> ----8<----
>
> (Save as  "whatever_test.go" and run `go test`.)
>
> --
> You received this message because you are subscribed to the Google Groups 
> "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to golang-nuts+unsubscr...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to