在 Bash 中捕获 stdout 和 stderr
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13806626/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Capture both stdout and stderr in Bash
提问by djechlin
I know this syntax
我知道这个语法
var=`myscript.sh`
or
或者
var=$(myscript.sh)
Will capture the result (stdout
) of myscript.sh
into var
. I could redirect stderr
into stdout
if I wanted to capture both. How to save each of them to separate variables?
将捕获into的结果 ( stdout
) 。如果我想同时捕获两者,我可以重定向到。如何将它们中的每一个保存到单独的变量中?myscript.sh
var
stderr
stdout
My use case here is if the return code is nonzero I want to echo stderr
and suppress otherwise. There may be other ways to do this but this approach seems it will work, if it's actually possible.
我的用例是,如果返回码非零,我想回显stderr
和抑制否则。可能还有其他方法可以做到这一点,但如果确实可能,这种方法似乎会奏效。
采纳答案by zb'
There is no way to capture both without temp file.
如果没有临时文件,则无法同时捕获两者。
You can capture stderr to variable and pass stdout to user screen (sample from here):
您可以将 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
But there is no way to capture both stdout and stderr:
但是没有办法同时捕获 stdout 和 stderr:
What you cannot do is capture stdout in one variable, and stderr in another, using only FD redirections. You mustuse a temporary file (or a named pipe) to achieve that one.
你不能做的是在一个变量中捕获 stdout,在另一个变量中捕获 stderr,只使用 FD 重定向。您必须使用临时文件(或命名管道)来实现该文件。
回答by gniourf_gniourf
There's a really ugly way to capture stderr
and stdout
in two separate variables without temporary files (if you like plumbing), using process substitution, source
, and declare
appropriately. I'll call your command banana
. You can mimic such a command with a function:
这里是一个非常丑陋的方式来捕捉stderr
和stdout
在没有临时文件的两个单独的参数(如果你喜欢管道),使用过程中替换,source
和declare
适当的。我会打电话给你的命令banana
。您可以使用函数模拟这样的命令:
banana() {
echo "banana to stdout"
echo >&2 "banana to stderr"
}
I'll assume you want standard output of banana
in variable bout
and standard error of banana
in variable berr
. Here's the magic that'll achieve that (Bash≥4 only):
我假设你想要的标准输出banana
变量bout
的标准误差banana
的变量berr
。这是实现这一目标的魔法(仅限 Bash≥4):
. <({ berr=$({ bout=$(banana); } 2>&1; declare -p bout >&2); declare -p berr; } 2>&1)
So, what's happening here?
那么,这里发生了什么?
Let's start from the innermost term:
让我们从最里面的术语开始:
bout=$(banana)
This is just the standard way to assign to bout
the standard output of banana
, the standard error being displayed on your terminal.
这只是分配给bout
的标准输出的标准方法banana
,标准错误显示在您的终端上。
Then:
然后:
{ bout=$(banana); } 2>&1
will still assign to bout
the stdout of banana
, but the stderr of banana
is displayed on terminal via stdout (thanks to the redirection 2>&1
.
仍将分配给bout
的标准输出banana
,但标准错误banana
是通过标准输出显示在终端上(感谢重定向2>&1
.
Then:
然后:
{ bout=$(banana); } 2>&1; declare -p bout >&2
will do as above, but will also display on the terminal (via stderr) the content of bout
with the declare
builtin: this will be reused soon.
将做如上述,但也将在终端显示(通过stderr)上的内容bout
与declare
内置:这将很快重新使用。
Then:
然后:
berr=$({ bout=$(banana); } 2>&1; declare -p bout >&2); declare -p berr
will assign to berr
the stderr of banana
and display the content of berr
with declare
.
将分配给berr
stderrbanana
并显示berr
with的内容declare
。
At this point, you'll have on your terminal screen:
此时,您将在终端屏幕上看到:
declare -- bout="banana to stdout"
declare -- berr="banana to stderr"
with the line
与线
declare -- bout="banana to stdout"
being displayed via stderr.
通过 stderr 显示。
A final redirection:
最后重定向:
{ berr=$({ bout=$(banana); } 2>&1; declare -p bout >&2); declare -p berr; } 2>&1
will have the previous displayed via stdout.
将通过标准输出显示前一个。
Finally, we use a process substitutionto sourcethe content of these lines.
最后,我们使用一个进程替换到源这些行的内容。
You mentioned the return code of the command too. Change banana
to:
您也提到了命令的返回码。更改banana
为:
banana() {
echo "banana to stdout"
echo >&2 "banana to stderr"
return 42
}
We'll also have the return code of banana
in the variable bret
like so:
我们还将banana
在变量中有返回代码,bret
如下所示:
. <({ berr=$({ bout=$(banana); bret=$?; } 2>&1; declare -p bout bret >&2); declare -p berr; } 2>&1)
You can do without sourcing and a process substitution by using eval
too (and it works with Bash<4 too):
您可以使用eval
too(它也适用于 Bash<4)而无需采购和流程替换:
eval "$({ berr=$({ bout=$(banana); bret=$?; } 2>&1; declare -p bout bret >&2); declare -p berr; } 2>&1)"
And all this is safe, because the only stuff we're source
ing or eval
ing are obtained from declare -p
and will always be properly escaped.
所有这一切都是安全的,因为我们正在source
或正在执行的唯一内容eval
是从中获得的,declare -p
并且总是会被正确地转义。
Of course, if you want the output in an array (e.g., with mapfile
, if you're using Bash≥4—otherwise replace mapfile
with a while
–read
loop), the adaptation is straightforward.
当然,如果您想要数组中的输出(例如,使用mapfile
,如果您使用 Bash≥4——否则替换mapfile
为while
–read
循环),调整很简单。
For example:
例如:
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)
and with return code:
并带有返回代码:
. <({ berr=$({ mapfile -t bout< <(banana; bret=$?; declare -p bret >&3); } 3>&2 2>&1; declare -p bout >&2); declare -p berr; } 2>&1)
回答by anubhava
You can do:
你可以做:
OUT=$(myscript.sh 2> errFile)
ERR=$(<errFile)
Now $OUT
will have standard output of your script and $ERR
has error output of your script.
现在$OUT
将有您的脚本的标准输出和您的脚本的$ERR
错误输出。
回答by jofel
An easy, but not elegant way: Redirect stderr to a temporary file and then read it back:
一种简单但不优雅的方法:将 stderr 重定向到临时文件,然后将其读回:
TMP=$(mktemp)
var=$(myscript.sh 2> "$TMP")
err=$(cat "$TMP")
rm "$TMP"
回答by Hyman
While I have not found a way to capture stderr and stdout to separate variables in bash, I send both to the same variable with…
虽然我还没有找到一种方法来捕获 stderr 和 stdout 以在 bash 中分离变量,但我将它们发送到同一个变量...
result=$( { grep "JUNK" ./junk.txt; } 2>&1 )
… then I check the exit status “$?”, and act appropriately on the data in $result.
……然后我检查退出状态“$?”,并对 $result 中的数据采取适当的行动。
回答by Tom Michaelis
# 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=
shift
# Name of shell variable to capture the stderr of command.
error=
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] = ##代码##
}
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")
}