【问题标题】:Why does a BASH arithmetic expression error cause infinite while loops to break?为什么 BASH 算术表达式错误会导致无限 while 循环中断?
【发布时间】:2022-09-24 20:57:16
【问题描述】:

为什么在 BASH 算术表达式 $((...)) 中执行错误的 HEX 转换会导致所有 while 循环中断?

例子:

#!/bin/bash

good_hex=\"ABCD\"
bad_hex=\"EFGH\"

while true;
do
    echo \"Start 1\"
    while true;
    do
        echo \"Start 2\"
        # Convert Hex to Decimal
        var1=$((16#$good_hex))
        echo \"Good Hex: $var1\"
        var2=$((16#$bad_hex))
        echo \"Bad Hex: $var2\"  # Won\'t be printed
        echo \"End 2\"           # Won\'t be printed
    done
    echo \"Exit 2\"              # Won\'t be printed
    echo \"End 1\"               # Won\'t be printed
done
echo \"Exit 1\"

输出:

chris@ubuntu:~$ ./hex_breaks.sh 
Start 1
Start 2
Good Hex: 43981
./hex_breaks.sh: line 15: 16#EFGH: value too great for base (error token is \"16#EFGH\")
Exit 1

在错误的十六进制转换之后,运行任何一个while循环都没有更多内容。执行的下一条语句是“Exit 1”(在所有 while 循环之外),然后程序终止。

作为比较,使用 \"let\" 命令而不是 $((...)) 会导致脚本正确运行并永远循环。

例子:

#!/bin/bash

good_hex=\"ABCD\"
bad_hex=\"EFGH\"

while true;
do
    echo \"Start 1\"
    while true;
    do
        echo \"Start 2\"
        # Convert Hex to Decimal
        let var1=16#$good_hex
        echo \"Good Hex: $var1\"
        let var2=16#$bad_hex
        echo \"Bad Hex: $var2\"  # Will be printed
        echo \"End 2\"           # Will be printed
    done
    echo \"Exit 2\"
    echo \"End 1\"
done
echo \"Exit 1\"

输出:

chris@ubuntu:~$ ./hex_works.sh 
Start 1
Start 2
Good Hex: 43981
./hex_works.sh: line 15: let: var2=16#EFGH: value too great for base (error token is \"16#EFGH\")
Bad Hex: 
End 2
Start 2
Good Hex: 43981
./hex_works.sh: line 15: let: var2=16#EFGH: value too great for base (error token is \"16#EFGH\")
Bad Hex: 
End 2
Start 2
Good Hex: 43981
./hex_works.sh: line 15: let: var2=16#EFGH: value too great for base (error token is \"16#EFGH\")
Bad Hex: 
End 2
Start 2
Good Hex: 43981
./hex_works.sh: line 15: let: var2=16#EFGH: value too great for base (error token is \"16#EFGH\")
Bad Hex: 
End 2

...

(Continues forever)

该脚本按预期运行,并且永远不会跳出 while 循环。

一些消息来源声称 \"let\" 和 $((...)) 是相同的。我的 lint 检查器说我应该使用 $((...)) 算术复合而不是 \"let\" 因为它更安全。然而,打破所有条件循环似乎是一个糟糕的操作完全出乎意料的副作用!

任何想法发生了什么?

  • 类似于while true; do; : \"${1a}\"; done

标签: bash shell


【解决方案1】:

一些消息来源声称“让”和 $((...)) 是相同的。

这不是真的。 let ... 是一个命令,而$((...))算术展开;后者用于构造前者,并且不会让它知道它是否成功地遵循命令,这与命令不同,后者在终止时设置$?。出于这个原因,当算术扩展失败时,POSIX shell 要么使包含它的顶级命令失败,要么根据它们是否是交互的而退出(有关更多信息,请参阅Consequences of Shell Errors)。这样外壳就不会具有破坏性;由于算术扩展不好,用户可以立即返回并修改他的脚本/命令,而不会做任何意外。

当不在POSIX mode 中时,bash 的行为就好像它是交互式的,并且使包含错误算术扩展的顶级命令失败并继续前进。在 POSIX 模式下,它的行为类似于其他 shell,并且不会打印您的 Exit 1

$ bash --posix hex_breaks.sh 
Start 1
Start 2
Good Hex: 43981
hex_breaks.sh: line 15: 16#EFGH: value too great for base (error token is "16#EFGH")
$

我的 lint 检查器说我应该使用 $((...)) 算术复合而不是“let”,因为它更安全。然而,打破所有条件循环似乎是一个糟糕的操作完全出乎意料的副作用!

您的 linter 可能正在谈论 ((...)) 复合命令;它们更类似于let 而不是$((...)) 扩展,并且只有在... 部分中存在语法错误时才会失败。试试((var2 = 16#$bad_hex)),你会看到的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多