Home > Software design >  /bin/sh: capture stderr into a variable
/bin/sh: capture stderr into a variable

Time:09-11

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

CodePudding user response:

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

CodePudding user response:

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.

CodePudding user response:

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 :-)

  • Related