Capturing `stderr` during Fish's Command Substitution
I've been working on extending Spack's fish-shell support, and I came
across an unusual problem in fish: when I use a nested version of command
substitution (ie. the braces-operator (cmd)), the outer command Substitution
refused to capture stderr. This can be demonstrated using the (rather dumb)
following example:
function cause_error
echo "this is a message"
echo "this is another message"
echo "this is an error" 1>&2
return 1
end
function spam
if set output (cause_error)
echo "no error: $status"
else
echo "error: $status"
end
end
To my frustration, I found that calling:
set all_output (spam 2>&1)
does not re-route this is an error into all_output as one might expect.
Instead what happens is that, since the command Substitution in spam (if set
output (cause_error)) does not redirect stderr, the stderr output from
cause_error cannot be captured by set all_output (spam 2>&1). Currently
this seems to be a bug in fish, so I had to be creative by writing
my one capture_all version of eval (see below).
Quick and Dirty Solution
While we're waiting for the fish folks to fix this bug, the following
function runs a command using eval and captures status,
stdout, and stderr.
function capture_all
begin;
begin;
eval $argv[1]
set $argv[2] $status # read sets the `status` flag => capture here
end 2>| read -z __err
end 1>| read -z __out
# output arrays
set $argv[3] (echo $__out | string split \n)
set $argv[4] (echo $__err | string split \n)
return 0
end
It's a bit cumbersome (because everything is managed manually), but it should
do the job. Everything is passed using global variables (that the user
supplies). The command needs to be passed a single string, and the user-defined
target variables need to be set using set -g beforehand. For example:
set -g stat
set -g out
set -g err
capture_all "cause_error" stat out err
should set
statto1out[1]tothis is a message, andout[2]tothis is another messageerr[1]tothis is an error.
where the purpose of stat is to monitor the execution of eval.