【问题标题】:Brace expansion with variable? [duplicate]带变量的大括号扩展? [复制]
【发布时间】:2013-10-26 07:10:48
【问题描述】:
#!/bin/sh
for i in {1..5}
do
   echo "Welcome"
done

会工作,显示 Welcome 5 次。

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

没用! howmany 将等于 5,因为这是 grep -c 的输出将显示的内容。 $1 是运行脚本时的参数 1。

有什么想法吗?

【问题讨论】:

  • 添加一个 echo $howmany 用于调试以检查它 IS 5 '不起作用' ==> 可能意味着什么,发送运行的完整输出。
  • 可以使用 seq 命令。对于 i in $(seq 1 $howmany)
  • @user2701753,我尝试过:x=5; for i in {1..$x}; do echo "Hi"; done,但也不起作用。它只打印一次Hi
  • 我做了一些建议的cmets:i.imgur.com/xCza7Fv.png你可以在图片中看到上面的输出。

标签: bash variables syntax variable-expansion


【解决方案1】:

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

  • 如果意图仅仅是迭代一个范围内的数字 - 如 OP 的情况 - 最佳选择是 使用大括号扩展,而是使用 bash 的 C 样式循环 - 请参阅 user000001's answer

    • 如果具体数字不重要,而您只需执行指定次数的循环体,Cole Tierney's answer 是一个选项。
  • 如果仍然需要使用大括号扩展

    • 如果您不需要列表中的数字具有前缀或后缀,请使用 seq 实用程序和 unquoted 命令替换(小警告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
    • 如果您确实需要列表中的数字具有前缀或后缀,您有多种选择:

      • 使用带有-f 选项的seq 实用程序提供printf 样式的格式字符串(如上用于零填充),或基于eval 的纯Bash 变通方法(需要额外注意!)或者在循环中构建一个数组,详情见this answer
      • 您还可以考虑通用地实现该功能,例如通过编写自定义 shell 函数或使用实用程序(如 awkperl)编写自定义脚本。

安全使用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's answer 上的 cmets):
      • [rare] 字符串:只允许单个英文字母;例如{a..c}
      • 数字仅限十进制整数;例如,{1..10}{10..1}{-1..2}
        • 前缀和后缀示例:A{1..3}# -> A1# A2# A3#
        • 损坏 变量示例:{$from..$to} # !! FAILS - $from$to 被解释为 文字,因此不能识别为单个字母或十进制整数 - no 执行大括号扩展(见下文)。
          • 相比之下,使用变量确实zshksh 中工作。
      • bash 4+ 增加了两个功能:
        • 可选增量步长值
          • echo A{1..5..2} -> A1 A3 A5 - 数字加 2
        • 能够零填充
          • echo A{001..003} -> A001 A002 A003
  • invalid 大括号表达式扩展(被视为常规不带引号的字符串,{} 被视为文字):

    • echo {} -> '{}' - 作为大括号表达式无效:至少需要 2 ,-分隔标记
      • 这允许使用不带引号的{}find,例如。
    • echo {1..$to} -> '{1..<value-of-$to>}' - 作为大括号表达式无效。在bash:不支持变量;但是,在kshzsh有效
    • (相比之下,fish 扩展 any {...} 序列;类似地,zsh 具有选项 BRACE_CCL(默认为关闭)用于扩展 individual {..} 中的字符,这实际上会导致 任何非空 {...} 序列的扩展。)

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 这是为什么呢?先展开变量,再展开括号表达式不是更有意义吗?
    • @Shahbaz 也许这是有道理的。但这就是他们设计语言的方式。
    • @Shahbaz 可能,但bash 根本没有。最初,大括号扩展是为将a{b,c}d 扩展为abd acd 之类的东西而设计的; {1..10} 语法是后来添加的,但扩展顺序已经固定。我怀疑将其更改为允许大括号内的参数被认为不值得麻烦。对于它的价值,zsh 确实允许在大括号扩展中进行参数扩展。
    • @chepner:很高兴知道;起初我很困惑,它确实 可以处理 string-token 列表 形式的变量:v1=a v2=b; echo {$v1,$v2} -> 'a b'。 Bash first 将其转换为标记列表 "$v1 $v2"(大括号扩展),然后 then 扩展变量引用(参数扩展),对吗?由于必须保留此扩展顺序以实现向后兼容性,因此无法使用 numeric-range 形式的变量,因为在不知道变量引用代表什么数字的情况下,bash 无法创建标记列表,因为它不不知道要创建什么以及创建多少令牌。
    • @chepner:也很高兴知道您可以zsh 中使用变量引用; ksh 同上。
    【解决方案3】:

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

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

    【讨论】:

    • seq 不是 POSIX 标准化工具。它根本不能保证安装在 POSIX 系统上,更不用说以任何特定方式运行了。
    【解决方案4】:

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

    for i in $(seq 1 $howmany) 
    

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

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

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

    【讨论】:

    • 你需要 "s not 's in the eval.for i in $(eval echo "{$start..10}")
    【解决方案5】:

    你也可以使用while循环:

    while ((howmany--)); do
       echo "Welcome"
    done
    

    【讨论】:

      【解决方案6】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多