【问题标题】:For loop with an argument based range具有基于参数的范围的 For 循环
【发布时间】:2014-09-24 14:12:56
【问题描述】:

我想对一组按字典顺序命名的文件(10 之前的 01-09)运行某些操作。我必须使用相当旧的 FreeBSD 版本(7.3),所以我不能使用像 echo {01..30}seq -w 1 30 这样的 yummies。

我找到的唯一可行的解​​决方案是printf "%02d " {1..30}。但是,我不明白为什么我不能使用$1$2 而不是130。当我运行我的脚本 (bash ~/myscript.sh 1 30) printf 说{1..30}: invalid number

AFAIK,bash 中的变量是无类型的,那么 printf 怎么不能接受整数参数作为整数呢?

【问题讨论】:

  • printf 将/不会接受什么都不是问题。这是一个问题,因为当大括号扩展发生时,参数扩展尚未发生。
  • BashPitfalls #33 提到了这一点:mywiki.wooledge.org/BashPitfalls#for_i_in_.7B1...24n.7D——那里的链接可能有助于更好地理解命令运行时发生的情况。
  • (顺便说一下,可以将变量标记为包含整数的;虽然几乎没有理由这样做,但实际上是 i> 存储了一些类型数据)。

标签: bash for-loop printf


【解决方案1】:

Bash 支持 C 风格的 for 循环:

s=1
e=30
for i in ((i=s; i<e; i++)); do printf "%02d " "$i"; done

您尝试的语法不起作用,因为大括号扩展发生在参数扩展之前,所以当shell尝试扩展{$1..$2}时,它仍然是字面意思{$1..$2} ,而不是{1..30}


@Kent 给出的答案有效,因为eval 可以追溯到解析过程的开始。我倾向于建议避免习惯性地使用它,因为eval can introduce hard-to-recognize bugs - 如果您的命令被列入白名单以由sudo$1 运行,例如'$(rm -rf /; echo 1)',C 风格的循环示例肯定会失败,而 eval 的例子……没那么多。

诚然,执行提权攻击的人可能无法访问您编写的 95% 的脚本,但剩下的 5% 确实会毁了一个人的一天; 100% 地遵循良好做法,避免养成草率的习惯。


因此,如果一个真的想要将一系列数字传递给单个命令,安全的做法是将它们收集在一个数组中:

a=( )
for i in ((i=s; i<e; i++)); do a+=( "$i" ); done
printf "%02d " "${a[@]}"

【讨论】:

    【解决方案2】:

    我猜你正在寻找这个技巧:

    #!/bin/bash
    s=1
    e=30
    printf "%02d "  $(eval echo {$s..$e})
    

    【讨论】:

      【解决方案3】:

      好的,我终于明白了!

      #!/bin/bash
      #BSD-only iteration method    
      #for day in `jot $1 $2`
      for ((day=$1; day<$2; day++))
      do
          echo $(printf %02d $day)
      done
      

      我最初想将循环迭代器用作文件名中的“日”,但现在我发现在我的确切情况下,迭代普通数字(1、2、3 等)并将它们处理成字典更容易循环内的。在使用jot 时,请记住$1 是数字数量,$2 是起点。

      【讨论】:

      • jot 是什么?它不是 POSIX 标准命令或 bash 内置命令。
      • 假设它只打印从第一个参数开始到第二个参数之前结束的一系列数字,那么安装 bash 的任何地方的工作方式都是for ((day=$1; day&lt;$2; day++))
      • (hmm -- 似乎是一个 BSDism;在 OS X 上可用,但在我戳过的 Linux 版本上不可用......也就是说,通过使用它,你正在减少你的脚本的可移植性)。
      • jotseq 的 FreeBSD 版本(不要问我是怎么发生的)。它的工作原理实际上是一样的,例如我使用joe 而不是nano,不是因为我讨厌nano,它只是没有安装,而joe 做同样的事情。 jotseq 之间存在细微的语法差异,但是,我添加了一些关于您应该传递给 jot 的内容的描述
      • 为什么是 echo $(printf ...) 而不是 printf ...?增加子shell的开销而没有好处;如果您想要一个尾随换行符,请在末尾添加一个; echo - 比fork()ing 启动子shell、reading 输出、waiting 关闭、字符串拆分和全局扩展该输出并将其设置为echo 的参数。
      猜你喜欢
      • 2016-10-31
      • 2016-12-01
      • 1970-01-01
      • 2014-12-06
      • 2014-01-12
      • 2013-01-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多