将数字范围读入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 脚本中有一个类似的循环。我正在为此任务使用 select
和 case
语句。 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
因此,我正在构建一个 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 脚本中有一个类似的循环。我正在为此任务使用 select
和 case
语句。 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