I think you want to capture stderr
, stdout
and exitcode
if that is your intention you can use this code:
## Capture error when 'some_command() is executed
some_command_with_err() {
echo 'this is the stdout'
echo 'this is the stderr' >&2
exit 1
}
run_command() {
{
IFS=$'\n' read -r -d '' stderr;
IFS=$'\n' read -r -d '' stdout;
IFS=$'\n' read -r -d '' stdexit;
} < <((printf '\0%s\0%d\0' "$(some_command_with_err)" "${?}" 1>&2) 2>&1)
stdexit=${stdexit:-0};
}
echo 'Run command:'
if ! run_command; then
## Show the values
typeset -p stdout stderr stdexit
else
typeset -p stdout stderr stdexit
fi
This scripts capture the stderr
, stdout
as well as the exitcode
.
But Teo how it works?
First, we capture the stdout
as well as the exitcode
using printf '\0%s\0%d\0'
. They are separated by the \0
aka 'null byte'.
After that, we redirect the printf
to stderr
by doing: 1>&2
and then we redirect all back to stdout
using 2>&1
. Therefore, the stdout
will look like:
"<stderr>\0<stdout>\0<exitcode>\0"
Enclosing the printf
command in <( ... )
performs process substitution. Process substitution allows a process’s input or output to be referred to using a filename. This means <( ... )
will pipe the stdout
of (printf '\0%s\0%d\0' "$(some_command_with_err)" "${?}" 1>&2) 2>&1
into the stdin
of the command group using the first <
.
Then, we can capture the piped stdout
from the stdin
of the command group with read
. This command reads a line from the file descriptor stdin
and split it into fields. Only the characters found in $IFS
are recognized as word delimiters. $IFS
or Internal Field Separator is a variable that determines how Bash recognizes fields, or word boundaries, when it interprets character strings. $IFS
defaults to whitespace (space, tab, and newline), but may be changed, for example, to parse a comma-separated data file. Note that $*
uses the first character held in $IFS.
## Shows whitespace as a single space, ^I(horizontal tab), and newline, and display "$" at end-of-line.
echo "$IFS" | cat -vte
# Output:
# ^I$
# $
## Reads commands from string and assign any arguments to pos params
bash -c 'set w x y z; IFS=":-;"; echo "$*"'
# Output:
# w:x:y:z
for l in $(printf %b 'a b\nc'); do echo "$l"; done
# Output:
# a
# b
# c
IFS=$'\n'; for l in $(printf %b 'a b\nc'); do echo "$l"; done
# Output:
# a b
# c
That is why we defined IFS=$'\n'
(newline) as delimiter.
Our script uses read -r -d ''
, where read -r
does not allow backslashes to escape any characters, and -d ''
continues until the first character ''
is read, rather than newline.
Finally, replace some_command_with_err
with your script file and you can capture and handle the stderr
, stdout
as well as the exitcode
as your will.