Alrighty! So the problem at its core is that you have two different places that you can get an error:
- When exec.Command().Output() can't run your shell script at all (because it doesn't have permissions in this case), and
- When your shell script itself runs, but returns an error code (because the last line run in the script is an error).
In the first case, you should get something reasonable in err because Go itself got a well-defined system error when trying to do something (which failed immediately). In the second case, Go was able to run your script, but it had output (that you're ignoring) and an return code (in this case, the exit code of the last thing executed by your script). Since the output could have literally been anything at all (including megabytes and megabytes of nonsense text), Go takes the lazy way out and just returns the status code. This, IMHO, is the right thing to do, given the rampant abuse of stderr by basically everyone.
Plus, your script (if modified slightly) could actually return an error code without displaying an error message at all!
That said, if you want to see the actual error information from your script, do one of the following:
// Display everything we got if error.
output, err := exec.Command(...).CombinedOutput()
if err != nil {
fmt.Println("Error when running command. Output:")
fmt.Println(string(output))
fmt.Printf("Got command status: %s
", err.Error())
return
}
or:
// Display just the stderr if an error occurs
cmd := exec.Command(...)
stderr := &bytes.Buffer{} // make sure to import bytes
cmd.Stderr = stderr
err := cmd.Run()
if err != nil {
fmt.Println("Error when running command. Error log:")
fmt.Println(stderr.String())
fmt.Printf("Got command status: %s
", err.Error())
return
}
...or you can go fancier and use StderrPipe() and just read the last line or similar. Really, there are a bunch of ways to skin this particular cat (since cmd.Stderr can be any io.Writer, you could easily write nearly any handler -- including one that parses the stream looking for root causes, etc etc).
Something to consider is that Output() and CombinedOutput() as used will display stuff only when the script is completely done running. This can be a serious UI problem with long-running scripts, as it means that any user monitoring what's going on will see a big, fat load of nothing while processing is happening. Using StdoutPipe() and StderrPipe() is likely preferable, as you can parse/interpret (or just display) output as it happens. Of course, if you can guarantee that your script won't run for more than a few seconds or that it won't be monitored by a human, who cares?
If you want to explore how to display output as the script runs, the example for StdoutPipe() is a pretty good place to start.