关于shell:bash set -e和i = 0;让i ++不一致

bash set -e and i=0;let i++ do not agree

仅当变量的先验值为零时,带调试选项'set -e -v'的以下脚本才在增量运算符处失败。

1
2
3
4
5
6
#!/bin/bash
set -e -v
i=1; let i++; echo"I am still here"
i=0; let i++; echo"I am still here"

i=0; ((i++)); echo"I am still here"

bash(GNU bash版本4.0.33(1)-发行版(x86_64-apple-darwin10)以及GNU bash版本4.2.4(1)-发行版(x86_64-unknown-linux-gnu)

有任何想法吗?


我的问题的答案不是使用let(或shift或...),而是使用

1
i=$((i+1))

当尝试通过设置'在非零状态代码上退出'来检查bash脚本时

1
set -e

bash手册指出set -e的作用是"如果简单命令以非零状态退出,则立即退出"。

不幸的是,let(和shift和...)返回计算结果("如果最后一个arg的值为0,则let返回1;否则返回0")。因此,获得状态返回值而不是状态码。有时,此返回值将为零,有时根据计算将为1。因此,set -e将导致脚本退出,具体取决于您的计算结果!除非您永远不使用它或求助于

1
let i++ || true

正如arnaud576875指出的那样,顺便说一句,这会增加CPU负担。

使用

1
let ++i

仅适用于i不为-1的特定情况,就像let i ++仅适用于i不为0的情况一样。因此,半解法。

虽然我喜欢Unix,但我别无选择。


如果let的最后一个参数求值为0,则让我们返回1(因此,非零状态):

From the manual:

1
   let arg [arg ...]

Each arg is an arithmetic expression to be evaluated. If the last arg evaluates to 0, let returns 1; 0 is returned otherwise.

i0时,i++的值为零(因为它是后递增的,因此返回了i的先前值),因此let返回1,并且由于set -e, bash存在。

以下是一些解决方案:

1
2
3
let ++i         # pre-increment, if you expect `i` to never be -1
let i++ 1       # add an expression evaluating to non-zero
let i++ || true # call true if let returns non-zero

查看set -e上的BASH手册页:

Exit immediately if a simple command (see SHELL GRAMMAR above) exits with a non-zero status. [...]

因此,如果有任何语句返回非零的退出代码,则外壳程序将退出。

let命令中查看BASH联机帮助页:

If the last arg evaluates to 0, let returns 1; 0 is returned otherwise.

可是等等! i++的答案是1而不是0!它应该起作用了!

同样,答案是使用增量运算符上的BASH联机帮助页:

id++ id--: variable post-increment and post-decrement

好吧,不太清楚。试试这个shell脚本:

1
2
3
4
5
6
#!/bin/bash
set -e -v
i=1; let ++i; echo"I am still here"
i=0; let ++i; echo"I am still here"

i=0; ((++i)); echo"I am still here"

嗯...按预期工作,我所做的就是在每行中将i++更改为++i

i++是后递增运算符。这意味着,它在let语句返回值之后递增i。由于i在递增之前为零,因此let语句返回一个非零值。

但是,++i是预递增运算符。这意味着它在返回退出状态之前递增i。由于i递增到1,所以退出状态变为零。

我希望这是有道理的。