os.exec.Command and non-nil return status

2,315 views
Skip to first unread message

Vasily Korytov

unread,
May 29, 2016, 3:48:57 PM5/29/16
to golang-nuts
Hi,

I do something like this:

import "os/exec"
out, err := exec.Command("/bin/false").CombinedOutput() // Not really this command.
// In fact here I parse stderr+out, but omitted for briefness.

The real problem is when os.exec.Command returns a non-zero return status, I've got error.
That is: there is no way of checking the command for specific non-zero return code.

How could I work around this? That is: I want to exec a command, check it's return status to be 1 (not 0 and not 2) and to parse it's stdout+err for some string.

Thanks.

Dave Cheney

unread,
May 29, 2016, 6:02:28 PM5/29/16
to golang-nuts
Once a command is executed the result of that execution are captured in exec.Command.ProcessState

Vasily Korytov

unread,
Jun 1, 2016, 1:07:27 PM6/1/16
to golang-nuts
понедельник, 30 мая 2016 г., 1:02:28 UTC+3 пользователь Dave Cheney написал:
Once a command is executed the result of that execution are captured in exec.Command.ProcessState


Thanks, it seems like I need the ProcessState.status.ExitStatus().

But how do I suppress `err` for non-null exit status?

Janne Snabb

unread,
Jun 1, 2016, 4:58:04 PM6/1/16
to golan...@googlegroups.com
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)
}
}


Janne Snabb
sn...@epipe.com
> --
> 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...@googlegroups.com
> <mailto:golang-nuts...@googlegroups.com>.
> For more options, visit https://groups.google.com/d/optout.

Konstantin Khomoutov

unread,
Jun 2, 2016, 7:15:17 AM6/2/16
to Janne Snabb, golan...@googlegroups.com
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`.)

jimmy frasche

unread,
Jul 3, 2016, 4:00:25 PM7/3/16
to Konstantin Khomoutov, Janne Snabb, golang-nuts
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.
> --
> 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...@googlegroups.com.

sebi...@gmail.com

unread,
May 5, 2017, 9:04:38 PM5/5/17
to golang-nuts, flat...@users.sourceforge.net, sn...@epipe.com
Just wanted to give a quick +1 here, came in handy for a use-case just now!
Reply all
Reply to author
Forward
0 new messages