我知道这种语法
var=`myscript.sh`
或者
var=$(myscript.sh)
将捕获into的结果 ( stdout
) 。如果我想同时捕获两者,我可以重定向到。如何将它们中的每一个保存到单独的变量中?myscript.sh
var
stderr
stdout
我的用例是如果返回码不为零,我想回显stderr
并抑制其他情况。可能有其他方法可以做到这一点,但这种方法似乎会奏效,如果它真的可能的话。
有一种非常丑陋的方法可以在没有临时文件(如果你喜欢管道)的情况下捕获两个单独的变量,使用stderr
进程替换, , 和适当的。我会打电话给你的命令。您可以使用函数模拟这样的命令:stdout
source
declare
banana
banana() {
echo "banana to stdout"
echo >&2 "banana to stderr"
}
我假设您想要 in variable 的标准输出和in banana
variablebout
的标准错误。这是实现这一目标的魔法(仅限 Bash≥4):banana
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
.
然后:
{ bout=$(banana); } 2>&1; declare -p bout >&2
会像上面那样做,但也会在终端上(通过stderr)显示内置的内容bout
:declare
这将很快被重用。
然后:
berr=$({ bout=$(banana); } 2>&1; declare -p bout >&2); declare -p berr
将分配给berr
stderrbanana
并显示berr
with的内容declare
。
此时,您将在终端屏幕上看到:
declare -- bout="banana to stdout"
declare -- berr="banana to stderr"
用线
declare -- bout="banana to stdout"
通过 stderr 显示。
最终重定向:
{ 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)
您也可以不使用采购和流程替换eval
(它也适用于 Bash<4):
eval "$({ berr=$({ bout=$(banana); bret=$?; } 2>&1; declare -p bout bret >&2); declare -p berr; } 2>&1)"
所有这一切都是安全的,因为我们正在source
ing 或eval
ing 的唯一东西是从中获得的,declare -p
并且总是会被适当地逃脱。
当然,如果您想要一个数组中的输出(例如,mapfile
如果您使用 Bash≥4,则使用 ,否则替换mapfile
为while
–<code>read 循环),调整很简单。
例如:
banana() {
printf 'banana to stdout %d\n' {1..10}
echo >&2 'banana to stderr'
return 42
}
. <({ berr=$({ mapfile -t bout < <(banana); } 2>&1; declare -p bout >&2); declare -p berr; } 2>&1)
并带有返回码:
. <({ berr=$({ mapfile -t bout< <(banana; bret=$?; declare -p bret >&3); } 3>&2 2>&1; declare -p bout >&2); declare -p berr; } 2>&1)
如果没有临时文件,就无法同时捕获两者。
您可以将 stderr 捕获到变量并将 stdout 传递给用户屏幕(来自此处的示例):
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
但是没有办法同时捕获标准输出和标准错误:
您不能做的是仅使用 FD 重定向在一个变量中捕获 stdout,在另一个变量中捕获 stderr。您必须使用临时文件(或命名管道)来实现该文件。
你可以做:
OUT=$(myscript.sh 2> errFile)
ERR=$(<errFile)
现在$OUT
将有你的脚本的标准输出和你的脚本$ERR
的错误输出。
一种简单但不优雅的方法:将 stderr 重定向到一个临时文件,然后将其读回:
TMP=$(mktemp)
var=$(myscript.sh 2> "$TMP")
err=$(cat "$TMP")
rm "$TMP"
虽然我还没有找到一种方法来捕获 stderr 和 stdout 以在 bash 中分隔变量,但我将两者发送到同一个变量......</p>
result=$( { grep "JUNK" ./junk.txt; } 2>&1 )
…然后我检查退出状态“$?”,并对 $result 中的数据采取适当的行动。
# 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")
}