关于bash:使用变量进行扩展?

Brace expansion with variable?

本问题已经有最佳答案,请猛点这里访问。
1
2
3
4
5
#!/bin/sh
for i in {1..5}
do
   echo"Welcome"
done

会工作,显示欢迎5次。

1
2
3
4
5
6
#!/bin/sh
howmany=`grep -c $1 /root/file`
for i in {1..$howmany}
do
   echo"Welcome"
done

不行! howmany将等于5,因为grep -c的输出将显示。 $ 1是参数1,在运行脚本时是特定的。

有任何想法吗?


无法在序列括号表达式中使用变量的变通方法:

  • 如果意图只是迭代一个范围内的数字 - 如OP的情况 - 最好的选择不是使用大括号扩展,而是使用bash的C风格循环 - 请参阅user000001的答案。

    • 如果具体的数字并不重要,你只需要执行指定次数的循环体,Cole Tierney的答案就是一个选项。
  • 如果需要使用支撑膨胀:

    • 如果您不需要列表中的数字具有前缀或后缀,请使用带有不带引号的命令替换的seq实用程序(小警告:seq不是POSIX实用程序,但它可以广泛使用);例如

      • echo $(seq 3) - > 1 2 3;起始编号1隐含

        • echo $(seq -f '%02.f' 3) - > 01 02 03 - 零填充
      • echo $(seq 2 4) - > 2 3 4;显式的开始和结束数字
      • echo $(seq 1 2 5) - > 1 3 5;自定义增量(中间的2)
    • 如果您需要列表中的数字有前缀或后缀,您有几个选择:

      • 使用seq实用程序及其-f选项提供printf样式的格式字符串(如上面用于零填充),或基于eval的纯Bash解决方法(需要额外注意!)或构建循环中的数组,所有这些都在这个答案中详述。
      • 您还可以考虑一般性地实现该功能,例如通过编写自定义shell函数或使用诸如awkperl之类的实用程序的自定义脚本。

使用驱动序列括号表达式的变量安全使用eval的示例:

事先验证变量,以确保它们包含十进制整数。

1
2
3
4
5
6
from=1 to=3  # sample values

# Ensure that $from and $to are decimal numbers and abort, if they are not.
(( 10#$from + 10#$to || 1 )) 2>/dev/null || { echo"Need decimal integers">&2; exit 1; }

eval echo"A{$from..$to}"  # -> 'A1 A2 A3'

支架扩展概述

大括号扩展的主要目的是扩展到一个令牌列表,每个令牌都有一个可选的前缀和/或后缀;大括号扩展必须是不加引号的,有两种风格:

  • 逗号分隔字符串的固定系列(列表) - 支持的变量

    • 指定并扩展为固定数量的令牌(2个或更多);例如。:
    • echo A{b,c,d} - > Ab Ac Ad,即3个令牌,如args的数量所暗示的那样。
    • echo {/,$HOME/}Library,例如, - > /Library /User/jdoe/Library
    • 支持变量引用 - 甚至是全局 - 但请注意,在正常评估过程中,在结果扩展后,它们会扩展。
  • 带有..的序列表达式(范围),通常是数字 - 不支持的变量

    • 扩展为可变数量的标记,由文字起点和终点驱动(由于历史原因,不支持使用变量 - 请参阅有关user000001答案的注释):

      • [罕见]字符串:仅允许单个英文字母;例如{a..c}
      • 数字:仅十进制整数;例如,{1..10}{10..1}{-1..2}

        • 带前缀和后缀的示例:A{1..3}# - > A1# A2# A3#
        • 带有变量的破坏示例:{$from..$to} # !! FAILS - $from$to被解释为文字,因此不会被识别为单个字母或十进制整数 - 不执行大括号扩展(参见下文)。

          • 相反,使用变量确实可以在zshksh中使用。
      • bash 4+增加了两个功能:

        • 可选的增量步长值:

          • echo A{1..5..2} - > A1 A3 A5 - 数字加2
        • 零垫的能力:

          • echo A{001..003} - > A001 A002 A003
  • 无法扩展无效的大括号表达式(将其视为常规的不带引号的字符串,将{}视为文字):

    • echo {} - > '{}' - 作为大括号expr无效:至少需要2个, - 分离的标记

      • 例如,这允许使用带有find的未加引号的{}
    • echo {1..$to} - > '{1..}' - 作为大括号expr无效。在bash中:不支持变量;但是,在kshzsh中有效。
    • (fish,相比之下,扩展任何{...}序列;类似地,zsh具有选项BRACE_CCL(默认为OFF),用于扩展{..}内的单个字符,这有效地导致任何非空{...}序列的扩展。)

在扩展变量之前评估大括号扩展。 你需要一个c风格的循环代替:

1
2
3
4
for ((i=1;i<=howmany;i++))
do
   echo"Welcome"
done


问题是"支撑扩展"是在"可变扩展"之前执行的

1
for i in $(seq 1 $howmany)

作为@damienfrancois说,或者,如果你愿意:

1
for i in $(eval echo '{$start..10}')

可能会这样做,但不要将它用于每个人的理智。


创建一个序列来控制你的循环

1
2
3
for i in $(seq 1 $howmany); do
echo"Welcome";
done


你也可以使用while循环:

1
2
3
while ((howmany--)); do
   echo"Welcome"
done

在这种情况下我们也可以使用eval

1
2
3
4
howmany=`grep -c $1 /root/file`
for i in $(eval echo {1..$howmany}); do
    echo"Welcome"
done