2

In my bash script, I read in a set of lines that look like this

arg $PROG arg arg

I want to be able to run the line, capture the STDERR as a variable, and prevent either the STDOUT or STDERR from printing to the screen. This is my solution so far but the error variable is always empty.

$PROG=/c/Program1
{ error=$($(eval $line) 2>&1 1>&$out); } {out}>&1
echo $error

Please explain solutions thoroughly. I am new to bash shell scripting and trying to learn.

Benjamin W.
  • 46,058
  • 19
  • 106
  • 116
  • I offered [Store/capture stdout and stderr in different variables](http://stackoverflow.com/q/11027679) as a duplicate, but you could consider [Redirect stderr and stdout in a Bash script](https://stackoverflow.com/questions/637827/redirect-stderr-and-stdout-in-a-bash-script) and [How to pipe stderr and not stdout](https://stackoverflow.com/questions/2342826/how-to-pipe-stderr-and-not-stdout) too. – Jonathan Leffler Apr 09 '16 at 21:23

1 Answers1

6

For a command that prints to both stdout and stderr like this one:

mycommand () { echo "stdout"; echo "stderr" >&2; }

If I issue that normally, I see all the output:

$ mycommand
stdout
stderr

I can redirect stdout or stderr to /dev/null so it doesn't get printed:

$ mycommand >/dev/null    # Equivalent to 1>/dev/null
stderr
$ mycommand 2>/dev/null
stdout

If I want to capture only stderr, I first have to redirect stderr to where stdout out is pointing to at the moment, then redirect stdout to /dev/null:

error="$(2>&1 1>/dev/null mycommand)"

Now error only contains output from stderr:

$ echo "$error"
stderr

The position on the line doesn't really matter, but the order of redirections does, so this has the same effect:

error="$(mycommand 2>&1 1>/dev/null)"

but this doesn't:

error="$(1>/dev/null 2>&1 mycommand)"

The last command would redirect both stdout and stderr to /dev/null, and error would be empty.

A great resource for redirection is on the Bash Hackers Wiki.


A few pointers for what you tried:

  • Assignment requires to not use the $ in the parameter name, so it should be PROG=/c/Program1 and not $PROG=/c/Program1.
  • Uppercase variable names are discouraged because they are more likely to clash with environment variables.
  • In 99% of the cases, there is a solution that avoids eval, see BashFAQ/048 why that is a good idea.
Benjamin W.
  • 46,058
  • 19
  • 106
  • 116
  • It doesn't work in some cases? Like process substitution `diff -u <(2>&1 1>/dev/null echo Hello stderr) <(echo Hello stdout)` – Sandburg Oct 09 '20 at 08:20
  • @Sandburg `2>&1 1>/dev/null echo Hello stderr` prints nothing because `echo` prints to stdout, and that is redirected to `/dev/null`. All the examples use `mycommand`, which is a command that prints output to both stdout and stderr. – Benjamin W. Oct 09 '20 at 13:06