将数字范围读入for循环

Read range of numbers into a for loop

因此,我正在构建一个 bash 脚本,该脚本遍历以 1 到 9 的数字命名的文件夹。该脚本取决于用户输入的文件夹名称。我的意图是使用 for loop 使用 read 输入来获取文件夹名称或一系列文件夹名称,然后做一些事情。

示例:
假设我想用 rsync -a 一定范围的文件夹进行备份。通常我会这样做:

for p in {1..7}; do
     rsync -a $p/* backup.$p
done

以上将递归备份目录1 2 3 4 5 6 和7 中的所有内容,并将它们放入名为'backup.{index-number}' 的文件夹中。它不会用前导 . 捕获 folders/files 但这现在并不重要。

现在我在交互式 bash 脚本中有一个类似的循环。我正在为此任务使用 selectcase 语句。 case 中的一个选项是这个循环,它将以某种方式从用户输入中获取一系列数字。这现在成了一个问题。

问题:
如果我使用 read 来获取范围,那么在使用 {1..7} 作为输入时它会失败。输入按字面意思,输出只是:

{1..7}

我真的很想知道为什么会这样。让我使用一个带有简单 echo 命令的更具描述性的示例。

var={1..7} # fails and just outputs {1..7}
for p in $var; do echo $p;done

read var # Same result as above. Just outputs {1..7}
for p in $var; do echo $p;done

for p in {1..7}; do echo $p;done # works fine and outputs the numbers 1-7 seperated with a newline.

我通过将数字存储在数组中找到了解决方法。然后用户可以输入由 space 字符分隔的文件夹名称,如下所示:1 2 3 4 5 6 7

read -a var # In this case the output is similar to the 3rd loop above
for p in ${var[@]}; do echo $p; done

这可能是一种可行的方法,但是当备份 1-40 的 40 个文件夹然后将所有数字一个一个地相加时,我的脚本完全多余。人们可以同时找到解决千年问题之一的方法。

有什么方法可以 read 像 {1..9} 这样的数字范围,或者是否可以有另一种方法将终端的输入输入到脚本中,以便我可以遍历for-loop?

内的范围

这听起来像是 google 的问题,但我显然使用了错误的模式来获得有用的答案。 SO 上的大多数类似问题都涉及大括号和参数扩展问题,但这并不是我遇到的问题。然而,对我来说,这个问题的答案似乎朝着类似的方向发展。我不明白为什么当 for-loop{1..7} 分配给变量时有效,但像 var={1..7} 那样做却不起作用。请帮助-.-

编辑:我的 bash 版本:

$ echo $BASH_VERSION
4.2.25(1)-release

EDIT2:大括号扩展的多功能性对我来说非常重要。一个可能的解决方案应该包括定义尽可能多的范围的能力。就像我希望能够选择仅备份 1 个文件夹或 f.ex 4-22 之间的固定范围,甚至多个选项,如文件夹 1,2,5,6-7

不在变量右侧或参数展开时执行大括号展开。使用 C 风格的 for 循环,必要时用户输入范围的上限。

read upper
for ((i=1; i<=$upper; i++)); do

输入由空格分隔的下限和上限

read lower upper
for (i=$lower; i <= $upper; i++)); do

对于任意一组值,只需将生成适当列表的负担推给用户;不要尝试实现自己的解析器来处理类似 1,2,20-22:

的内容
while read p; do
    rsync -a $p/* backup.$p
done

每行输入一个值,如

1
2
20
21
22

即使用户正在使用 shell,他们也可以使用类似

的方式调用您的脚本
printf '%s\n' 1 2 20..22 | backup.sh

用户生成列表比您安全地解析字符串描述列表更容易。

邪恶eval

$ var={1..7}
$ for i in $(eval echo $var); do echo $i; done

这也行,

$ var="1 2 {5..9}"
$ for i in $(eval echo $var); do echo $i; done
1
2
5
6
7
8
9

evil eval 是一个笑话,也就是说,只要你知道你在评估什么。

或者,awk

$ echo "1 2 5-9 22-25" | 
  awk -v RS=' '  '/-/{split([=12=],a,"-"); while(a[1]<=a[2]) print a[1]++; next}1'

1
2
5
6
7
8
9
22
23
24
25