为什么 bash 大括号扩展在某些算术表达式中起作用,而在其他算术表达式中不起作用?

Why does bash brace expansion work in some arithmetic expressions but not others?

我正在编写一个非常简单的 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")

我会很感激在这方面的一些帮助...

$(())用于算术表达式,算术中不进行大括号扩展。

创建一个带循环的数组:

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

正在设置 1990 年的返程机。

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

Bash... implemented $[...] because there was no other syntax at the time, and to gain some operational experience with arithmetic expansion in the shell. Bash-1.14... lists both forms of arithmetic expansion, but by the time bash-2.0 was released in 1995, the manual referred only to the $((...)) form.

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

Chet 在同一主题中明确表示 $[] 表格已过时,但并未弃用:

Now, it's hardly any problem to keep dragging the $[...] syntax along. It takes only a few dozen bytes of code. I have no plans to remove it.

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

In early proposals, a form $[expression] was used. It was functionally equivalent to the "$(())" of the current text, but objections were lodged that the 1988 KornShell had already implemented "$(())" and there was no compelling reason to invent yet another syntax. Furthermore, the "$[]" syntax had a minor incompatibility involving the patterns in case statements.

因此 bash 中的实施行为并不完全符合规范,但由于没有删除它的计划,如果它巧妙地解决了您的问题,我认为没有理由放弃它的好处。但是,正如@Barmar 的评论所指出的那样,在此处对代码进行注释并 link 是个好主意,这样未来的开发人员就知道您到底是什么意思了!

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

由于参数在 bash 中展开的顺序而不起作用。大括号展开 ({}) 比算术展开 ($(())) 早 bash。如果是其他方式,您的代码肯定会工作。

来自man bash

The order of expansions is: brace expansion; tilde expansion, parameter and variable expansion, arithmetic expansion, and command substitution (done in a left-to-right fashion); word splitting; and pathname expansion.

运行 像这样 bash。这个问题要求解释,而不是解决方案,但这里将是一种 $(()) 表达方式。

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

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

(bash 手动)

To avoid conflicts with parameter expansion, the string ‘${’ is not considered eligible for brace expansion, and inhibits brace expansion until the closing ‘}’.