【问题标题】:bash set -e and i=0;let i++ do not agreebash set -e and i=0;让i++不同意
【发布时间】:2011-11-07 00:25:31
【问题描述】:

只有当变量的先前值为零时,带有调试选项“set -e -v”的以下脚本才会在增量运算符处失败。

#!/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)-release (x86_64-apple-darwin10) 以及 GNU bash,版本 4.2.4(1)-release (x86_64-unknown-linux-gnu))

有什么想法吗?

【问题讨论】:

  • 我从来没有想过一个看似无害的增量会与set -e发生这种意外的交互。幸运的是我发现了这个问题。

标签: bash shell set operator-keyword increment


【解决方案1】:

如果let 的最后一个参数计算为0,让返回1(因此,非零状态):

来自手册:

   let arg [arg ...]

每个 arg 都是要计算的算术表达式。 如果最后一个 arg 计算结果为 0,则 let 返回 1;否则返回 0。

i0 时,i++ 的计算结果为零(因为它是一个后增量,所以返回之前的i 值),所以let 返回1,并且由于@ 987654331@,bash 存在。

这里有一些解决方案:

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

【讨论】:

    【解决方案2】:

    看着BASH manpage上的set -e

    如果一个简单的命令(参见上面的 SHELL GRAMMAR)以非零状态退出,则立即退出。 [...]

    因此,如果任何语句返回非零退出代码,shell 将退出。

    看看BASH manpage,在let命令上:

    如果最后一个 arg 的值为 0,则 let 返回 1;否则返回 0。

    但是等等! i++ 的答案是 1 而不是 0!应该可以的!

    同样,答案是关于增量运算符的 BASH 手册页:

    id++ id--:变量后增后减

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

    #!/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,退出状态变为零。

    我希望这是有道理的。

    【讨论】:

    • 没有意义的是 let、set -e 和通情达理的人不应该生活在一起,因为没有人会写 'let i++ || true' 除非作为电子邮件签名。
    • 事实上i++不是一个自增操作符,而是一个post-increment操作符,会抛出人。大多数人不知道这一点,除非他们是 C 程序员并且他们已经看到:foo = bar + i++;oldfoo = foo++ 的次数足以让他们患上脑瘤。
    • 不,不是这样,它是一个状态码,'set -e'用于检查运行状况,操作是否成功完成,用于传递有关结果的信息计算。开发人员认为它是一个特性而不是一个 bug,一个 la:让 i++ || echo 'look no hands',但最终结果是我们需要重写代码才能进行调试 - 事实上我不再相信这一点,因为更多的操作正在遵循这一点。顺便说一句,作为一致性问题,bash for((i=0;i
    • 对,是状态码!状态码返回before 增量完成,因为这是一个post-increment 操作符。由于i 为零,因此状态码为1。非常有道理——尤其是在大量饮酒之后。我同意它令人困惑并导致很多错误。这就是为什么我告诉人们不要使用i++,而是使用++i,因为它没有这些有趣的副作用。或者更好的i=$i+i,它可能同样高效,并且非常清楚你想要什么。
    【解决方案3】:

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

    i=$((i+1))
    

    当尝试通过设置“退出非零状态代码”来检查 bash 脚本时,

    set -e
    

    bash 手册指出 set -e 具有“如果简单命令以非零状态退出,则立即退出。”的效果。

    不幸的是,let(以及 shift 和 ...)返回计算结果('如果最后一个 arg 计算结果为 0,let 返回 1;否则返回 0')。所以不是状态码,而是得到某种返回值。有时这个返回值将是零,有时是一个取决于计算。因此 set -e 将导致脚本根据您的计算结果退出!!!除非你永远不使用它或求助于它,否则没有什么可做的

    let i++ || true
    

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

    使用

    let ++i
    

    仅适用于 i 不为 -1 的特定情况,如 let i++ 仅适用于 i 不为 0 时。因此是半解。

    虽然我喜欢 Unix,但我不会有任何其他方式。

    【讨论】:

    • +1 这个答案是正确的。我制作了一个测试脚本来检查我在互联网和 SO(大约 10 个)上找到的其他增量选项。在线脚本位于此处:goo.gl/XYzrK2
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-04-24
    • 1970-01-01
    • 2018-09-12
    • 1970-01-01
    • 1970-01-01
    • 2012-11-11
    • 2016-07-01
    相关资源
    最近更新 更多