如何使用大括号扩展变量

时间:2015-11-03 04:08:29

标签: linux bash shell brace-expansion

我有四个文件:

1.txt  2.txt  3.txt  4.txt
在linux shell中,我可以使用: ls {1..4}.txt列出所有四个文件 但如果我设置两个变量:var1 = 1和var2 = 4,如何列出这四个文件? 那就是:

var1=1
var2=4
ls {$var1..$var2}.txt  # error

什么是正确的代码?

2 个答案:

答案 0 :(得分:5)

变量brace expansion 序列表达式{<numFrom>..<numTo>})一起使用仅适用于{{ 1}}和ksh,但不幸的是,不在zsh (并且(大多数情况下)仅限POSIX功能 - 仅限bash这样的shell不支持支持扩展 at all ,因此应该完全避免使用dash扩展大括号。

鉴于您的症状,我假设您正在使用/bin/sh,您只能在序列表达式中使用文字 (例如{{1} }});来自manual(强调我的):

  

在任何其他扩展之前执行大括号展开,并且在结果中保留对其他扩展特殊的任何字符。

换句话说:在评估大括号表达式时,变量引用尚未扩展(已解决);解释文字,例如bash{1..3},因为序列表达式的上下文中的数字因此失败,因此大括号表达式被视为无效且未展开
但请注意,变量引用扩展的,即在整体扩展的后期阶段;在手边的情况下,字面结果是单个单词$var1 - 一个展开变量值的未展开大括号表达式。

虽然 list 形式的大括号扩展(例如,$var2)以相同的方式扩展,但后来的变量扩展不是问题,因为没有对列表的解释前期需要元素;例如'{1..4}'正确生成了2个字{foo,bar){$var1,$var2} 至于为什么变量不能在序列表达式中使用:历史上,首先是 list 形式的大括号扩展,当后来引入序列表达形式时,顺序为扩张已经确定 有关大括号扩展的一般概述,请参阅this answer

变通方法

注意:变通方法重点关注数字序列表达式,如问题所示;基于1的变通方法还演示了如何使用不太常见的字符序列表达式的变量,这些表达式产生英语字母的范围(例如4产生eval)。

可以使用基于{a..c}的解决方法as demonstrated in Jameson's answer

一个小小的警告是a b c不是POSIX实用程序,但大多数现代的类Unix平台都有它。 功能

要稍微优化一下,使用seq seq选项提供seq样式格式字符串,并演示两位数的零填充:

-f

请注意,为了使其完全健壮 - 如果生成的单词包含空格或制表符 - 您需要使用嵌入式引用

printf

seq -f '%02.f.txt' $var1 $var2 | xargs ls # '%02.f'==zero-pad to 2 digits, no decimal places 然后看到seq -f '"%02.f a.txt"' $var1 $var2 | xargs ls ls,...正确保留了参数边界。

如果您想首先在Bash 数组中稳健地收集结果单词,例如01 a.txt

02 a.txt

以下是纯Bash解决方法:

仅使用Bash功能的 有限的变通办法使用${words[@]}

IFS=$'\n' read -d '' -ra words < <(seq -f '%02.f.txt' $var1 $var2)
ls "${words[@]}"

您可以将类似的技术应用于 字符序列表达式;

eval

注意:

  • 预先执行检查以确保var1=1 var2=4 # Safety check (( 10#$var1 + 10#$var2 || 1 )) 2>/dev/null || { echo "Need decimal integers." >&2; exit 1; } ls $(eval printf '%s\ ' "{$var1..$var2}.txt") # -> ls 1.txt 2.txt 3.txt 4.txt var1=a var2=c # Safety check [[ $var1 == [a-zA-Z] && $var2 == [a-zA-Z] ]] || { echo "Need single letters."; exit 1; } ls $(eval printf '%s\ ' "{$var1..$var2}.txt") # -> ls a.txt b.txt c.txt 包含十进制整数或单个英文字母,这样可以安全地使用$var1通常情况下,使用未经检查的输入$var2会带来安全风险,因此最好避免使用eval
  • 鉴于来自eval 的输出必须未加引号传递给eval,以便shell通过单词将输出拆分为单个参数-splitting,仅当生成的文件名不包含嵌入空格或其他shell元字符时才有效。

更强大但更麻烦的纯Bash解决方法使用数组来创建等效词:

eval
  • 此方法不存在安全风险,也适用于带有嵌入式shell元字符的结果文件名,例如空格。
  • 自定义增量可以通过将ls替换为例如var1=1 var2=4 # Emulate brace sequence expression using an array. args=() for (( i = var1; i <= var2; i++ )); do args+=( "$i.txt" ) done ls "${args[@]}" 以2为增量来实现。
  • 实施零填充需要使用i++;例如,如下:
    i+=2

答案 1 :(得分:2)

对于那段特定的语法(&#34;序列表达式&#34;),你运气不好,请参阅Bash man page

  

序列表达式采用{x..y [.. incr]}的形式,其中x和y是          整数或单个字符,incr,可选的增量,          是一个整数。

但是,您可以使用seq实用程序,它会产生类似的效果 - 并且该方法允许使用变量:

var1=1
var2=4
for i in `seq $var1 $var2`; do
    ls ${i}.txt
done

或者,如果打电话给ls四次而不是一次困扰你,和/或你想把它全部放在一行上,那就像:

for i in `seq $var1 $var2`; do echo ${i}.txt; done | xargs ls

来自seq(1)手册页:

   seq [OPTION]... LAST
   seq [OPTION]... FIRST LAST
   seq [OPTION]... FIRST INCREMENT LAST