如何结合算术扩展和大括号扩展?

如何结合算术扩展和大括号扩展?

人们可以将算术扩展和大括号扩展结合起来吗?

$ for i in {$((1 + 1))..5}; do echo $i; done;
{2..5}
$ echo "bash laughs at me"

答案1

man bash解释见:

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

大括号扩展发生在算术扩展之前,因此您无法按照您尝试的方式组合它们。

改用seq

for i in $(seq $((a+4)) 12) ; do echo $i ; done

答案2

eval 'for i in {'"$((1 + 1))"'..5}; do echo $i; done'

输出

2
3
4
5

一般来说,如果您希望 shell 在实际执行某些操作之前执行某些操作,则必须让 shell 再检查一下。这是eval的功能 - 它两次评估一个命令。 shell 解析其对 shell 扩展的求值——这通常不会发生。

考虑一下:

v='"quotes in here"'; printf %s\\n $v
"quotes
in
here"

您会看到引号由 shell 的解析器解释,该解析器处理输入进行了扩展,因此其中的引号$v没有任何意义 - 没有解析器来理解它们。但如果我这样做:

v='"quotes in here"'; eval "printf '%s\\n' $v"
quotes in here

输出突然不同了。区别在于解析器 - 它决定其输入的哪些位是命令以及为什么。这是与;${}()||''""&& while for if所有此类东西一起使用的部分。这并不意味着{}后面的大括号与您所询问的大括号相同 - 正如其他答案已经显示的那样,这些大括号显然是作为bash扩展单独处理的。无论如何,通常您需要某种形式的第二次评估来处理这个问题。

正是这一点才造成了eval危险。当您"${expansion}"引用扩展时,您将其标记为解析器 - 您划界它。 shell 知道,无论发生什么,这都只是命令中的一项(可能是一个参数)。即使你不这样做; shell 不会接受$expansion超出;简单命令边界的命令 - 因为该简单命令也已经是分隔的。但是 - 就像它所做的那样eval- 如果 shell 返回并再次查看该扩展,它可能会找到可以运行的命令 -甚至几个- 这就是eval工作原理。

因此,在使用时eval必须非常小心,不要意外地两次评估 shell 令牌或任何可能包含一个的扩展- 并且您只在第一次循环时评估外壳不能足够快地完成的部分,否则。最佳实践是仅在不同时间单独评估命令的任何一个部分,如下所示。

eval又是第一个字符串:

eval \                    #inital command
'for i in {'\             #hard-quoted - not expanded or executed
"$((1 + 1))"\             #double-quoted - expanded and delimited
'..5}; do echo $i; done'  #hard-quoted - not expanded or executed

上面的评论讨论了在第一轮中采取的行动eval- 还有一个要进行。在这种情况下,您想要1+1.所以我们首先扩展它,而不是其他。我们能摆脱的最坏情况是2。这不是 shell 解析器标记,可以安全地求值 - 甚至可以用双引号引起来。事实上,如果eval说它有任何真正有效的用途的话,那就是算术。其他所有内容都是硬引用的 - 不采取任何操作,只是连接字符串并删除引号,就像任何其他硬引用字符串一样。

但是当它第二次返回时——在第一次传递中删除引号之后——shell 发现:

for i in {2..5}; do echo $i; done

它确实做到了。

还有其他方法可以解决这个问题:

bash -c "for i in {$((1+1))..5}; do echo \$i; done"

^有效。

和:

. /dev/fd/0 <<CMD
    for i in {$((1+1))..5}; do echo \$i; done
CMD

^有效。在每种情况下,逻辑都是相同的 - 在评估大括号扩展之前评估变量bash

不过,我自己从来没有大量使用过支架扩展。我通常喜欢:

until [ $((i=$i+1)) -gt 5 ]
do  printf %d\\n $i
done

相关内容