2

I am assigning the output of a command to variable A:

A=$(some_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
400 the Cat
  • 266
  • 3
  • 23
  • You may find your answer here : https://stackoverflow.com/questions/2342826/how-can-i-pipe-stderr-and-not-stdout – Alceste_ Sep 10 '22 at 04:51
  • https://stackoverflow.com/questions/3130375/bash-script-store-stderr-in-a-variable – fver1004 Sep 10 '22 at 04:53
  • I see answers how to redirect `stderr` to file, and then read it back into variable form that file. There must be better solution than that. – 400 the Cat Sep 10 '22 at 05:11
  • There is probably no other way, because it is meant to be output und thus designed that way. You are already bending things with your current approach. Maybe it would help to analyze your use case and see why you think you need that functionality. Maybe Perl oder Python would fit your needs better. – Boris Däppen Sep 10 '22 at 08:03
  • Here are some interesting suggestions wich might help with your approach of read: https://stackoverflow.com/a/19142644/1694803 – Boris Däppen Sep 10 '22 at 08:10

3 Answers3

2

Here's a code snippet that might help you


#  capture stderr into a variable and print it
echo "capture stderr into a variable and print it"
var=$(lt -l /tmp 2>&1)
echo $var
capture stderr into a variable and print it
zsh: command not found: lt

#  capture stdout into a variable and print it
echo "capture stdout into a variable and print it"
var=$(ls -l /tmp)
echo $var

#  capture both stderr and stdout into a variable and print it
echo "capture both stderr and stdout into a variable and print it"
var=$(ls -l /tmp 2>&1)
echo $var




# more classic way of executing a command which I always follow is as follows. This way I am always in control of what is going on and can act accordingly

if somecommand ; then
    echo "command succeeded"
else
    echo "command failed"
fi

If you have to capture the output and stderr in different variables, then the following might help as well


## create a file using file descriptor for stdout
exec 3> stdout.txt
# create a file using file descriptor for stderr
exec 4> stderr.txt

A=$($1 /tmp 2>&4 >&3);

## close file descriptor
exec 3>&-
exec 4>&-

## open file descriptor for reading
exec 3< stdout.txt
exec 4< stderr.txt

## read from file using file descriptor
read line <&3
read line2 <&4

## close file descriptor
exec 3<&-
exec 4<&-

## print line read from file
echo "stdout: $line"
echo "stderr: $line2"

## delete file

rm stdout.txt
rm stderr.txt

You can try running it with the following

╰─ bash test.sh pwd
stdout: /tmp/somedir
stderr: 

╰─ bash test.sh pwdd
stdout: 
stderr: test.sh: line 8: pwdd: command not found
codeaprendiz
  • 2,703
  • 1
  • 25
  • 49
1

As noted in a comment your use case may be better served in other scripting languages. An example: in Perl you can achieve what you want quite simple:

#!/usr/bin/env perl
use v5.26;                   # or earlier versions
use Capture::Tiny 'capture'; # library is not in core

my $cmd = 'date';
my @arg = ('-R', '-u');
 
my ($stdout, $stderr, $exit) = capture {
  system( $cmd, @arg );
};

say "STDOUT: $stdout";
say "STDERR: $stderr";
say "EXIT:   $exit";

I'm sure similar solutions are available in python, ruby, and all the rest.

Boris Däppen
  • 1,186
  • 7
  • 20
1

I gave it another try using process substitution and came up with this:

# command with no error
date +%b > >(read A; if [ "$A" = 'Sep' ]; then echo 'September'; fi ) 2> >(read B; if [ ! -z "$B" ]; then echo "$B"; fi >&2)
September

# command with error
date b > >(read A; if [ "$A" = 'Sep' ]; then echo 'September'; fi ) 2> >(read B; if [ ! -z "$B" ]; then echo "$B"; fi >&2)
date: invalid date “b“

# command with both at the same time should work too

I had no success "exporting" the variables from the subprocesses back to the original script. It might be possible though. I just couldn't figure it out. But this gives you at least access to stdout and stderr as a variable. This means you can do whatever processing you want on them as variables. It depends on your use case if this is of any help to you. Good luck :-)

Boris Däppen
  • 1,186
  • 7
  • 20