如何在Bash中与set -o errexit一起使用local?

3

我刚接触Bash,发现当我使用set -o errexit命令时,如果一个命令失败,脚本并没有退出。后来发现是因为我声明了一个局部变量!

请告诉我如何同时使用局部变量和set -o errexit命令。

示例:

#!/bin/bash
set -o errexit
set -o nounset
set -o pipefail

function test {
 local output=$(ls --badoption)
 echo "error code is $?"
 echo "output=$output"
}

test

结果:

./test.sh
ls: illegal option -- -
usage: ls [-ABCFGHLOPRSTUWabcdefghiklmnopqrstuwx1] [file ...]
error code is 0
output=

但是:

#!/bin/bash
set -o errexit
set -o nounset
set -o pipefail

function test {
 output=$(ls --badoption)
 echo "error code is $?"
 echo "output=$output"
}

test

结果:

ls: illegal option -- -
usage: ls [-ABCFGHLOPRSTUWabcdefghiklmnopqrstuwx1] [file ...]

我不确定,但我认为这是因为-o errexit仅适用于命令是简单命令(如bash手册页中定义的)的情况,而在声明本地变量时运行命令并不是作为简单命令运行。 - blm
这是非常重要的一点 -o errexit。听起来好像如果我用local写好代码,就会失去功能。希望社区能找到一个解决办法。另外,local与命令本身没有太多关系,它只是一个变量作用域。 - Sebastien Tardif
1
是的,我并不是在为这种行为辩护,只是在指出这种行为的本质。然而,有一个简单的解决方法,我已经在答案中提供了。 - blm
2个回答

4
由于`set -o errexit`(或使用`-e`运行bash)仅影响简单命令(如bash手册中所定义)。在`local output=$(cmd)`中,`cmd`不被视为简单的命令,因此`-e`没有任何作用(`$?`与`cmd`退出方式无关)。一个简单的解决方法是通过替换来拆分该行代码:
local output=$(ls --badoption)

使用:

local output

output=$(ls --badoption)

在我进行的测试中,它可以做到你想要的一切,它将立即退出,如果你用ls替换ls --badoption,它就可以工作了,并且在调用test之前设置output,然后在之后回显它,显示output确实是test本地的。

太好了,它正在运行。想知道为什么我的问题被负评了。我提供了可执行代码,我认为这个问题从来没有得到过答案,如果Bash的设计者在测试的话,这段代码应该可以工作... - Sebastien Tardif
命令替换的退出状态丢失是有意为之的。如果不是local本身的退出状态,你认为像local x=$(exit 2) y=$(exit 3) z=$(exit 4)这样的东西的退出状态应该是什么? - chepner

3
local 的文档说明如下:

除非在函数外使用,提供无效的名称或 name 是只读变量,否则返回状态为零。

因此,local 命令的返回状态不受命令替换执行成功与否的影响。
正如 blm 在他的回答中所指出的,您可以通过分离本地变量的声明和赋值来获得想要的效果。普通赋值使用命令替换的返回状态是执行命令的子 shell 的返回状态。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接