我认为您想捕获 stderr、stdout 和 exitcode,如果这是您的意图,您可以使用此代码:
## 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
此脚本捕获stderr、stdout 以及exitcode。
但是 Teo 它是如何工作的?
首先,我们使用printf '\0%s\0%d\0' 捕获stdout 和exitcode。它们由 \0 aka 'null byte' 分隔。
之后,我们将printf 重定向到stderr,方法是:1>&2,然后我们使用2>&1 将所有重定向回stdout。因此,stdout 看起来像:
"<stderr>\0<stdout>\0<exitcode>\0"
将printf 命令包含在<( ... ) 中执行process substitution。进程替换允许使用文件名引用进程的输入或输出。这意味着<( ... ) 将使用第一个< 将(printf '\0%s\0%d\0' "$(some_command_with_err)" "${?}" 1>&2) 2>&1 的stdout 传递到command group 的stdin。
然后,我们可以使用read 从命令组的stdin 中捕获管道stdout。此命令从文件描述符stdin 中读取一行并将其拆分为字段。只有在 $IFS 中找到的字符被识别为单词分隔符。 $IFS 或 Internal Field Separator 是一个变量,用于确定 Bash 在解释字符串时如何识别字段或单词边界。 $IFS 默认为空格(空格、制表符和换行符),但可以更改,例如,解析逗号分隔的数据文件。请注意,$* 使用 $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
这就是我们将IFS=$'\n'(换行符)定义为分隔符的原因。
我们的脚本使用read -r -d '',其中read -r 不允许反斜杠转义任何字符,-d '' 一直持续到第一个字符'' 被读取,而不是换行符。
最后,将some_command_with_err替换为你的脚本文件,你就可以随心所欲地捕获和处理stderr、stdout和exitcode。