【问题标题】:Floating point comparison broken by locale change between bash 3.2 and 4.3bash 3.2 和 4.3 之间的语言环境更改破坏了浮点比较
【发布时间】:2021-06-26 21:56:00
【问题描述】:
$ echo $BASH_VERSION
3.2.57(1)-release
$ [[ "1.9" < "11.0" ]] && echo yes
yes
$

$ echo $BASH_VERSION
4.3.11(1)-release
$ [[ "1.9" < "11.0" ]] && echo yes
$

这是“为什么”它自 4.1 起不起作用:

Within double brackets, the > and < string comparison operators now conform to the locale.

这太荒谬了。这是一个很好的旧 ASCII 数字字符串,整个世界都希望在字符串比较中句点 [.] 小于 [0-9]。我们说的是哪个地区?

现在的挑战是如何删除/替换这个神话般的语言环境,以使 Bash 4.x 在不使用自定义函数的情况下产生“是”?

有很多使用自定义函数的解决方案,例如this elegant one using sort -V。然而,这个挑战是关于将 Bash's undocumented locale feature 配置成它只知道 1970 年的 7 位 ASCII 字符集 - 句点小于数字。

【问题讨论】:

  • 将数字作为字符串而不是数字进行比较从来没有期望首先起作用(而且通常,它不起作用;@ 987654327@ 即使在 bash 3 中也是如此,因为它是逐位工作的,因此如果存在较大的值在数字上较小的数字中具有较早的位置,那么 . 在排序顺序中的行为是无关紧要的)
  • no,bash 4 中的语言环境行为是not 未记录的。 (ABS 在所有方面都是错误的;它永远不应该被用作参考。官方参考是信息页面和手册;高质量的非官方参考是 bash-hackers' wiki 和 Woolledge wiki,但是值得注意的是不是 ABS,它经常过时,经常完全错误,并且非常经常因展示不良实践示例而感到内疚)。
  • ...有关相关文档,请参阅手册第 6.4 节“Bash 条件表达式”。引用:当与[[ 一起使用时,&lt;&gt; 运算符使用当前语言环境按字典顺序排序。测试命令使用 ASCII 排序。 -- 黑字文档; ABS 完全错误。
  • 顺便说一句,BashFAQ #22 描述了最佳实践浮点比较机制。
  • 另外,wooledge.org/~greybot/meta/abs 在#bash IRC 频道(以前是 freenode,现在是 Libera Chat)中有 abs factoid 的历史。请注意时间戳列——自 2008 年以来,人们一直一致认为 ABS 是“臭名昭著”(对于没有足够专业知识的任何人都无法安全地使用它来识别它的许多地方是完全错误的)。

标签: bash string-comparison lexicographic


【解决方案1】:

即使在 bash 3.2 中,所描述的做法也不能可靠地工作

观察:

[[ "2.0" < "10.0" ]] && echo "yes"

...在 所有 版本的 bash 中为 false,无论是否更改了所描述的语言环境。


不依赖语言环境的本地实现

如果您真的想在内部执行此操作以在没有外部工具的情况下进行 bash,正确实现的最简单方法是对整数部分使用数字整数比较,然后对后面的数字进行字符串比较小数点。

冒着啰嗦的风险:

float_is_greater() {
    local a_dec a_man b_dec b_man
    a_dec=${1%%.*}; b_dec=${2%%.*}
    a_man=${1#*.};  b_man=${2#*.}
    (( ${a_dec:-0} > ${b_dec:-0} )) && return 0
    (( ${b_dec:-0} > ${a_dec:-0} )) && return 1
    [[ ${a_man} > ${b_man} ]] && return 0
    return 1
}

float_is_greater 11.0 1.9 && echo yes

使用外部工具实现符合 POSIX 标准

也就是说,通常的做法是简单地使用标准 POSIX 提供的工具和原生浮点数学支持。例如:

case $(echo '1.9 - 11.0' | bc) in
  -*) echo '1.9 is less than 11.0';;
esac

...采用BashFAQ #22 中给出的示例之一。

【讨论】:

    【解决方案2】:

    我的脚本将“1.9”与 JDK 版本字符串“11.0.11”进行比较,我预计 ASCII 句点 [.] 小于 [0-9]。我的用例不是关于浮点比较,而是查尔斯更改了我的问题标题。

    我为我的用例找到了最简单的解决方案,即切换到使用 ASCII 排序顺序的单个 [:

    $ echo $BASH_VERSION
    4.3.11(1)-release
    $ [ "1.9" \< "11.0.1" ] && echo "yes"
    yes
    $
    

    Here is a more narrow focus question about [[ string1 < string2 ]] 继续讨论。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多