18

I need to capture the output and error of a command in my bash script and know whether the command succeeded or not.

At the moment, I am capturing both like this:

output=$(mycommand 2>&1)

I then need to check the exit value of mycommand. If it failed, I need to do some stuff with the output, if the command succeeded, I don't need to touch the output.

Since I am capturing the output, checking $? is always a 0 since bash succeeded at capturing the output into the variable.

This is a very time sensitive script, so we are trying to avoid any slower solutions like outputting to a file and re-reading it in.

If I could capture stdout to one variable and stderr to another, that would solve my problem because I could just check if the error variable was empty or not.

Thanks.

mhost
  • 6,930
  • 5
  • 38
  • 45

2 Answers2

11

What version of bash are you using? The capture of the output has zero effect on the return code with my version, 4.1.5:

pax> false; echo $?
1
pax> echo $?
0
pax> x=$(false 2>&1) ; echo $?
1

It's not always a good idea to rely on standard error being non-empty to detect errors. Many programs don't output errors but rely solely on the return code.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • I forgot to mention that I pipe the output of my command to another. Although I can do that after wards in a separate command. Thanks. – mhost Sep 03 '10 at 03:35
  • 4
    Caution! Having `local x=$(false 2>&1) ; echo $?` or `set x=$(false 2>&1) ; echo $?` (note **`local/set`** at the begining) outputs `0` not `1` as the last command run is `local/set`. – Piotr Dobrogost Jun 18 '13 at 10:38
10

The problem only seems to manifest when the output is captured to a local variable within a function:

$ echo $BASH_VERSION
3.2.48(1)-release
$ false; echo $?
1
$ echo $?
0
$ x=$(false 2>&1) ; echo $?
1
$ function f {
> local x=$(false 2>&1) ; echo $?
> }
$ f
0
$ function g {
> x=$(false 2>&1) ; echo $?
> }
$ g
1

Notice that only function f, which captures x to a local, can express the behavior. Particularly, function g which does the same thing, but without the 'local' keyword, works.

One can therefore not use a local variable, and perhaps 'unset' it after use.

EDIT NVRAM points out that the local declaration can be made beforehand to avoid the issue:

$ function h {
>   local x
>   x=$(false 2>&1) ; echo $?
> }
$ h
1
phs
  • 10,687
  • 4
  • 58
  • 84
  • Very interesting! This happens in bash as well as dash (posix) shell. – Gregor Oct 19 '11 at 15:00
  • 3
    Actually $? is set for the local variable declaration, if you simply declare it by itself, _then_ assign it on a separate line it will work as you want. In other words, add **local x** above your line with **x=** – NVRAM Jan 31 '12 at 16:30