【问题标题】:Why does bash brace expansion work in some arithmetic expressions but not others?为什么 bash 大括号扩展在某些算术表达式中有效,而在其他表达式中无效?
【发布时间】:2017-02-23 07:32:21
【问题描述】:

我正在编写一个非常简单的 bash 脚本,但我无法理解为什么已弃用的 $[] 可以完美运行,而 $(()) 似乎破坏了整个过程。

我指的代码是:

for i in {1..10};
do 
    printf %4d $[{1..10}*i]
    echo
done

在这个版本中我没有任何问题,但我不想使用已弃用的 bash 元素,这就是我想切换到 $(()) 的原因。

不幸的是,一旦我将代码更改为:

printf %4d $(({1..10}*i))

我收到一个错误:

./script_bash.sh: line 8: {1..10}*i: syntax error: argument expected (error token is "{1..10}*i")

如果能在这方面提供一些帮助,我将不胜感激......

【问题讨论】:

  • $(()) 是一个算术表达式。它不会在那里进行常规通配符或大括号扩展。

标签: bash brace-expansion


【解决方案1】:

$(()) 用于算术表达式,大括号扩展不是在算术中完成的。

用循环制作一个数组:

for i in {1..10}
do
    vals=()
    for j in {1..10}
    do
        vals+=($((i*j)))
    done
    printf "%4d" ${vals[@]}
done

【讨论】:

  • 但是,另一方面,为什么在 $[] 中进行大括号扩展?
  • 这是我第一次听说$[]。我在 bash 手册中找不到它。
  • 它是 $(( 的已弃用伪等效项,请参阅 wiki.bash-hackers.org/scripting/obsolete
  • 所以,我想从技术上讲你的第一句话不是真的,因为表面上$[] 也是“算术表达式”。
  • 但我猜在他们创建它的时候,他们并不认为算术扩展是一个完全不同的上下文,有自己的语法。
【解决方案2】:

为 1990 年设置回归机器。

Bash 根据 POSIX P1003.2d9(大约 1990 年)实现了 $[] 语法,这是已发布的 P1003.2-1992 的草案。在草案和标准之间的两年时间里,POSIX 转而选择了 ksh88 $(()) 语法和行为。 Chet Ramey(bash 维护者)有话要说,back in 2012

Bash... 实现了 $[...] 因为没有其他的 当时的语法,并获得一些操作经验 shell中的算术扩展。 Bash-1.14... 列出了算术扩展的两种形式,但是通过 bash-2.0 发布时间是 1995 年,手册中仅提及 $((...)) 形式。

这向我表明$[] 形式是实验性的,当POSIX 采用$(()) 语法时,它的某些行为(如大括号扩展)被指定为遗忘。那些实验性行为被保留了下来,因为野外已经有依赖它们的脚本(请记住已经过去了 2 年多)。

Chet 在同一个帖子中明确指出 $[] 表单已过时,但并未弃用:

现在,继续拖动 $[...] 语法几乎没有任何问题。 它只需要几十个字节的代码。我没有删除它的计划。

current POSIX standard, C.2.6 Word Expansions > Arithmetic Expansion 提到了语法(强调我的):

在早期的提案中,使用了 $[expression] 形式。 它在功能上等同于当前文本的“$(())”,但有人反对 1988 年的 KornShell 已经实现了“$(())”,并且没有令人信服的理由发明另一种语法。此外,"$[]" 语法与 case 语句中的模式有一点不兼容。

因此,bash 中实现的行为并不完全符合规范,但由于没有计划删除它,如果它巧妙地解决了您的问题,我认为没有理由放弃它的好处。但是,正如@Barmar 的评论所指出的,最好在此处评论代码并将其链接,以便未来的开发人员知道您的意思!

【讨论】:

  • 虽然它没有被明确地弃用(实际上是否有一个弃用的功能列表?),它甚至没有在手册中提到这一事实是避免它的强烈迹象。即使它从未真正消失,其他程序员也不会理解它。我已经编写 shell 脚本 3 年了,我以前从未见过这种情况,我相信我并不孤单。与您链接到的 bash-hackers 页面上的其他功能相比,它们仍在手册中。
  • @Barmar 在当前的 POSIX 规范中提到,purportedly 一些发行版修补了 bash 手册页来提及它。尽管如此,我已经详细说明了我的用法“推荐”。
【解决方案3】:

printf %4d $(({1..10}*i))

由于参数在bash 中的展开顺序而不起作用。大括号扩展 ({}) 比 bash 的算术扩展 ($(())) 更早完成。如果换一种方式,您的代码肯定可以工作。

来自man bash

展开顺序为:大括号展开;波浪号扩展、参数和变量扩展、算术扩展和命令替换(以从左到右的方式完成);分词;和路径名扩展。

【讨论】:

    【解决方案4】:

    遇到了一些这样的 bash。这个问题需要解释,而不是解决方案,但这里将使用 $(()) 方式来表达这一点。

    for i in {1..10}; do
      printf %4d $(eval echo '$(('{1..10}'*i))')
      echo
    done
    

    大括号扩展在算术扩展中被禁止,就像在参数扩展中一样。

    (bash 手册)

    为避免与参数扩展冲突,字符串'${'不是 被认为有资格进行大括号扩展,并禁止大括号扩展 直到结束的“}”。

    【讨论】:

      猜你喜欢
      • 2011-01-25
      • 1970-01-01
      • 1970-01-01
      • 2011-01-31
      • 2020-04-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-23
      相关资源
      最近更新 更多