0

In my script, I have a complicated command that generates output both on stdout and stderr. I need to capture both into separate variables:

#!/bin/sh
A=$(command)

How can I "capture" stderr into a variable B ?

I have tried some variations with 2>&1 and read but that does not work:

A=$(some_command) 2>&1 | read B
echo $B

or

{ OUT="$(command)" 2>&1 | read B ; }
echo $B

The only way that works is to redirect stderr into a temporary file, and then read it back. But that seems like a dirty hack. Is there a way to do it without using temporary files?

UPDATE

to clarify, both stdout and stderr will be multi-line outputs.

Martin Vegter
  • 358
  • 75
  • 236
  • 411

1 Answers1

0

Honestly, using a file might be the simplest way to go. But let's make some assumptions here, and say you want stdout as a one-line variable, and don't care about stderr being 1 line (or else flatten it too). Then let's use a simple script for testing - where "2" goes to stderr and the other lines to stdout:

> cat outerr.sh
echo 1
echo 2 >&2
echo 3
> ./outerr.sh
1
2
3
> ./outerr.sh 2>/dev/null
1
3

Then you can do something like:

(echo $(./outerr.sh) # Collect and flatten the stdout output 
                     # Meanwhile stderr flows through the pipe
) 2>&1|{
        read err     # This assumes stderr is one line only
        read out
        echo "err=$err"
        echo "out=$out"
}

or if stderr could be multiline, then

(echo $(./outerr.sh) # Collect and flatten the stdout output 
                     # Meanwhile stderr flows through the pipe
) 2>&1|tac|{         # reverse the output so the stdout line comes first
        read out
        err=$(tac)   # reverse lines again, back to the original line order
        echo "err=$err"
        echo "out=$out"
}

with output

err=2
out=1 3

If stdout lines need to be preserved, you could e.g. embed \n line breaks, or again, just go back to using a file instead.