我知道这个语法
var=`myscript.sh`
或
var=$(myscript.sh)
将stdout
的结果(myscript.sh
)捕获到var
。如果我想捕获两者,我可以将stderr
重定向到stdout
。如何将每个保存为单独的变量?
我的用例是,如果返回代码非零,我想回显stderr
并禁止其他方式。可能有其他方法可以做到这一点,但这种方法似乎可行,如果它确实可行。
答案 0 :(得分:37)
使用process substitution,stderr
和{有两种单独的变量捕获stdout
和source
而没有临时文件(如果您喜欢管道),这是一种非常难看的方法{1}}恰当。我会叫你的命令declare
。您可以使用函数模仿这样的命令:
banana
我假设您希望变量banana() {
echo "banana to stdout"
echo >&2 "banana to stderr"
}
中的banana
标准输出和变量bout
中的标准错误banana
。这是实现这一目标的魔力(仅限Bash≥4):
berr
那么,这里发生了什么?
让我们从最内层的术语开始:
. <({ berr=$({ bout=$(banana); } 2>&1; declare -p bout >&2); declare -p berr; } 2>&1)
这只是分配给bout=$(banana)
bout
标准输出的标准方式,即终端上显示的标准错误。
然后:
banana
仍会将{ bout=$(banana); } 2>&1
的标准分配给bout
,但banana
的标准显示在终端上通过标准输出显示(感谢重定向banana
。
然后:
2>&1
将如上所述,但也将在终端上(通过stderr)显示内置{ bout=$(banana); } 2>&1; declare -p bout >&2
bout
的内容:这将很快重复使用。
然后:
declare
将berr=$({ bout=$(banana); } 2>&1; declare -p bout >&2); declare -p berr
分配给berr
stderr,并banana
显示berr
的内容。
此时,您将进入终端屏幕:
declare
行
declare -- bout="banana to stdout"
declare -- berr="banana to stderr"
通过stderr显示。
最终重定向:
declare -- bout="banana to stdout"
将通过stdout显示之前的内容。
最后,我们使用process substitution 来源这些行的内容。
您也提到了命令的返回码。将{ berr=$({ bout=$(banana); } 2>&1; declare -p bout >&2); declare -p berr; } 2>&1
更改为:
banana
我们还会在变量banana() {
echo "banana to stdout"
echo >&2 "banana to stderr"
return 42
}
中返回banana
的返回码,如下所示:
bret
您也可以不使用. <({ berr=$({ bout=$(banana); bret=$?; } 2>&1; declare -p bout bret >&2); declare -p berr; } 2>&1)
进行采购和流程替换(并且它也适用于Bash&lt; 4):
eval
所有这些都是安全的,因为我们eval "$({ berr=$({ bout=$(banana); bret=$?; } 2>&1; declare -p bout bret >&2); declare -p berr; } 2>&1)"
或source
的唯一内容是从eval
获得的,并且始终会被正确转义。
当然,如果你想要一个数组中的输出(例如,使用declare -p
,如果你使用Bash≥4 - 否则将mapfile
替换为mapfile
- {{ 1}}循环),适应很简单。
例如:
while
并带有返回码:
read
答案 1 :(得分:26)
没有临时文件就无法捕获它们。
您可以将stderr捕获到变量并将stdout传递给用户屏幕(来自here的示例):
exec 3>&1 # Save the place that stdout (1) points to.
output=$(command 2>&1 1>&3) # Run command. stderr is captured.
exec 3>&- # Close FD #3.
# Or this alternative, which captures stderr, letting stdout through:
{ output=$(command 2>&1 1>&3-) ;} 3>&1
但是没有办法捕获stdout和stderr:
你不能做的是在一个变量中捕获stdout,在另一个变量中捕获stderr,仅使用FD重定向。您必须使用临时文件(或命名管道)来实现该文件。
答案 2 :(得分:15)
你可以这样做:
OUT=$(myscript.sh 2> errFile)
ERR=$(<errFile)
现在$OUT
将拥有您脚本的标准输出,而$ERR
会出现您脚本的错误输出。
答案 3 :(得分:7)
一种简单但不优雅的方法:将stderr重定向到临时文件,然后将其读回:
TMP=$(mktemp)
var=$(myscript.sh 2> "$TMP")
err=$(cat "$TMP")
rm "$TMP"
答案 4 :(得分:3)
虽然我还没有找到一种方法来捕获stderr和stdout来分隔bash中的变量,但我将两者都发送到同一个变量...
result=$( { grep "JUNK" ./junk.txt; } 2>&1 )
...然后我检查退出状态“$?”,并对$ result中的数据采取适当的行动。
答案 5 :(得分:-1)
# NAME
# capture - capture the stdout and stderr output of a command
# SYNOPSIS
# capture <result> <error> <command>
# DESCRIPTION
# This shell function captures the stdout and stderr output of <command> in
# the shell variables <result> and <error>.
# ARGUMENTS
# <result> - the name of the shell variable to capture stdout
# <error> - the name of the shell variable to capture stderr
# <command> - the command to execute
# ENVIRONMENT
# The following variables are mdified in the caller's context:
# - <result>
# - <error>
# RESULT
# Retuns the exit code of <command>.
# SOURCE
capture ()
{
# Name of shell variable to capture the stdout of command.
result=$1
shift
# Name of shell variable to capture the stderr of command.
error=$1
shift
# Local AWK program to extract the error, the result, and the exit code
# parts of the captured output of command.
local evaloutput='
{
output [NR] = $0
}
END \
{
firstresultline = NR - output [NR - 1] - 1
if (Var == "error") \
{
for (i = 1; i < firstresultline; ++ i)
{
printf ("%s\n", output [i])
}
}
else if (Var == "result") \
{
for (i = firstresultline; i < NR - 1; ++ i)
{
printf ("%s\n", output [i])
}
}
else \
{
printf ("%d", output [NR])
}
}'
# Capture the stderr and stdout output of command, as well as its exit code.
local output="$(
{
local stdout
stdout="$($*)"
local exitcode=$?
printf "\n%s\n%d\n%d\n" \
"$stdout" "$(echo "$stdout" | wc -l)" "$exitcode"
} 2>&1)"
# extract the stderr, the stdout, and the exit code parts of the captured
# output of command.
printf -v $error "%s" \
"$(echo "$output" | gawk -v Var="error" "$evaloutput")"
printf -v $result "%s" \
"$(echo "$output" | gawk -v Var="result" "$evaloutput")"
return $(echo "$output" | gawk "$evaloutput")
}