22
#!/bin/sh
for i in {1..5}
do
   echo "Welcome"
done

会工作,显示欢迎 5 次。

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

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

有任何想法吗?

4

6 回答 6

24

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

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

    • 如果具体数字不重要,并且您只需要执行指定次数的循环体,那么Cole Tierney 的答案是一个选项。
  • 如果仍然需要使用大括号扩展:

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

      • 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-style 格式字符串(如上面用于零填充),或基于纯 Bash 变通办法eval(需要额外注意!)或在循环中构建数组,所有这些都在此详述回答
      • 您还可以考虑通用地实现该功能,例如通过编写自定义 shell 函数或使用实用程序(如awk或)编写自定义脚本perl

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

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

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 种风格

  • 逗号分隔字符串的固定系列(列表) -支持变量
    • 指定并扩展为固定数量的令牌(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 {}-> '{}'- 作为大括号表达式无效:至少需要2 个 ,分隔标记
      • 例如,这允许使用 unquoted {}with find
    • echo {1..$to}-> '{1..<value-of-$to>}'- 作为大括号表达式无效。in bash:不支持变量;但是,在和中有效kshzsh
    • fish相比之下,扩展任何 {...}序列;类似地,具有用于扩展内部单个字符的zsh选项BRACE_CCL(默认为关闭),这有效地导致任何非空序列的扩展。){..} {...}
于 2015-03-12T22:55:53.323 回答
18

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

for ((i=1;i<=howmany;i++))
do
   echo "Welcome"
done
于 2013-10-17T17:00:07.097 回答
11

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

for i in $(seq 1 $howmany); do
echo "Welcome";
done
于 2013-10-17T17:00:05.560 回答
9

问题是在“变量扩展”之前执行“大括号扩展”

for i in $(seq 1 $howmany) 

正如@damienfrancois 所说的那样工作,或者,如果您愿意:

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

可能会,但不要为了每个人的理智而使用它。

于 2013-10-17T17:04:16.743 回答
8

您还可以使用 while 循环:

while ((howmany--)); do
   echo "Welcome"
done
于 2013-10-17T18:13:45.427 回答
1

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

howmany=`grep -c $1 /root/file`
for i in $(eval echo {1..$howmany}); do
    echo "Welcome"
done
于 2017-01-09T18:46:18.933 回答