使用“bash -e”时如何获取子shell的输出和退出值?

jab*_*sad 96 bash

考虑以下代码

外部作用域.sh

#!/bin/bash
set -e
source inner-scope.sh
echo $(inner)
echo "I thought I would've died :("
Run Code Online (Sandbox Code Playgroud)

内部作用域.sh

#!/bin/bash
function inner() { echo "winner"; return 1; }
Run Code Online (Sandbox Code Playgroud)

我试图outer-scope.sh在调用inner()失败时退出。由于$()调用子外壳,这不会发生。

如何在保留函数可能以非零退出代码退出的事实的同时获得函数的输出?

use*_*686 135

$()保留退出状态;您只需要在没有自身状态的语句中使用它,例如赋值。

输出=$(内部)

在此之后,$?将包含 的退出状态inner,您可以对其进行各种检查:

output=$(inner) || exit $?
echo $output
Run Code Online (Sandbox Code Playgroud)

或者:

if ! output=$(inner); then
    exit $?
fi
echo $output
Run Code Online (Sandbox Code Playgroud)

或者:

if output=$(inner); then
    echo $output
else
    exit $?
fi
Run Code Online (Sandbox Code Playgroud)

(注意:exit不带参数的bare等价于exit $?——也就是说,它以最后一个命令的退出状态退出。我使用第二种形式只是为了清楚起见。)


另外,为了记录:source在这种情况下完全无关。您可以只inner()outer-scope.sh文件中定义,结果相同。

  • 我不假思索地认为既然 `foo=$(...)` 是安全的,那么 `local foo=$(...)` 就会安全,但事实并非如此。我花了很长时间才考虑到“本地”可能是问题所在。编辑:我看到现在在 ryenus 的回答中提到了这一点 (5认同)
  • `如果!输出=$(内部);然后退出 $?; fi` 将以 0 的返回码退出,因为 `$?` 将给出 `!` 的返回码而不是 `inner` 的返回码。您可以使用 `if output=$(inner); 获得所需的行为;然后 : ; 否则退出 $?; fi` 但这显然更冗长 (4认同)
  • 嗯,当我输入`if !$(退出1); 然后回声 $?; fi`,我得到`0`。如果您需要保留该退出值,则不确定 `if` 是要走的路。 (2认同)

rye*_*nus 42

BashFAQ/002

如果您同时想要(输出和退出状态):

output=$(command)
status=$? 
Run Code Online (Sandbox Code Playgroud)

特例

注意函数局部变量的棘手情况,比较以下代码:

f() { local    v=$(echo data; false); echo output:$v, status:$?; }
g() { local v; v=$(echo data; false); echo output:$v, status:$?; }
Run Code Online (Sandbox Code Playgroud)

我们会得到:

$ f     # fooled by 'local' with inline initialization
output:data, status:0

$ g     # a good one
output:data, status:1
Run Code Online (Sandbox Code Playgroud)

为什么?

当子shell的输出用于初始化local变量时,退出状态不再是子shell的,而是命令的,最有可能是.local 0

另见/sf/answers/309489771/

  • 虽然这并没有真正回答问题,但这对我今天很有用,所以 +1。 (7认同)
  • 哇,我刚才遇到了这个确切的问题,你已经解决了。谢谢! (4认同)
  • bash 命令的退出状态始终是最后执行的命令的退出状态。当我们在强类型语言上花费大量时间时,很容易忘记“local”不是类型说明符而只是另一个命令。感谢您在这里重申这一点,今天帮助了我。 (3认同)

Dan*_*eck 5

#!/bin/bash
set -e
source inner-scope.sh
foo=$(inner)
echo $foo
echo "I thought I would've died :("
Run Code Online (Sandbox Code Playgroud)

通过添加echo,子 shell 不会独立运行(不会单独检查)并且不会中止。赋值可以避免这个问题。

您也可以执行此操作,并将输出重定向到文件,以便稍后处理它。

tmpfile=$( mktemp )
inner > $tmpfile
cat $tmpfile
rm $tmpfile
Run Code Online (Sandbox Code Playgroud)