昨天注册了SegmentFault问答网站,看到一个同学问Shell里怎么输出指定的数字序列,原问题是这样的:

Shell里怎么输出指定的数字序列:

for i in {1..5}; do echo $i; done

可以输出

1
2
3
4
5

但是如果

END=5
for i in {1..$END}; do echo $i; done

就不灵了。。。
怎么才能通过变量传一个区间进来,让他输出数字序列?

查了下文档,知道为什么{1..$END}没有效果了,看GNU的bash手册是这么说的:

Brace expansion is erformed before any other expansions and any characters special to other expansions are preserved in the result. It is strictly textual. Bash does not apply any syntactic interpretation to the context of the expansion or the text between the braces. To avoid conflicts with parameter expansion, the string ‘${’ is not considered eligible for brace expansion.

也就是说Brace expansion是在其它所有类型的展开之前处理的包括参数和变量展开,因此{1..$END}就是{1..$END},原封不动,不知道你能不能理解?或许你将{1..$END}看成{1..END}好了,因为这里$END只是一个字符串而已。

另外一方面,GNU的bash手册也说明了这个语法的使用方法:

A sequence expression takes the form {x..y[..incr]}, where x and y are either integers or single characters, and incr, an optional increment, is an integer. When integers are supplied, the expression expands to each number between x and y, inclusive....When characters are supplied, the expression expands to each character lexicographically between x and y, inclusive. Note that both x and y must be of the same type. When the increment is supplied, it is used as the difference between each term. The default increment is 1 or -1 as appropriate.

从上面可以看出{x..y}的限制条件是:1) 整数或者单个字符; 2)两者要是同一个类型。回到我们的例子,{1..$END}x=1, y=$END,前者是整数,后者是字符串,不符合以上任何一个条件,所以也就不展开了。

ABS文档里面也有例子介绍这个语法的使用方法,中间也提到这点。

$ for i in {1..$END}; do echo $i; done
{1..5}

从上面的结果可以看出,只是显示了{1..5}这个结果。如果要得到数值序列,有很多种方法。第一种是用seq start end这种形式来替换{start..end},如:

$ for i in `seq 1 $END`; do echo $i; done

当然如果你一定要用{start..end},可以用eval命令:

$ for i in `eval echo {1..$END}`; do echo $i; done

这里eval echo {1..$END}后的结果为{1..5}:

最直观的是用循环了:

$ for ((i=0;i<$END;i++)) do echo $i; done

当然,肯定有其它方法,只要你愿意去思考。