将变量与brace expansion的序列表达式形式 ({<numFrom>..<numTo>})一起使用仅适用于ksh和@987654327 @,但不幸的是,不在 bash 中(并且(大部分)严格来说,只有 POSIX 功能的 shell,例如 dash 不支持大括号扩展,所以应该完全避免使用/bin/sh 进行大括号扩展)。
鉴于您的症状,我假设您使用的是bash,您只能在序列表达式中使用文字(例如,{1..3});来自manual(强调我的):
大括号扩展在任何其他扩展之前执行,并且任何其他扩展的特殊字符都将保留在结果中。
换句话说:在计算大括号表达式时,变量引用尚未扩展(解析);因此,将文字(例如$var1 和$var2)解释为序列表达式上下文中的数字会失败,因此大括号表达式被视为无效且未展开。
但是请注意,变量引用是扩展的,即在整体扩展的后期;在手头的情况下,文字结果是单个单词 '{1..4}' - 一个未扩展的大括号表达式,其中扩展了变量值。
虽然 list 形式的大括号扩展(例如,{foo,bar))以相同的方式扩展,但后来的变量扩展不是问题,因为没有对列表元素的解释预先需要;例如{$var1,$var2} 正确地产生了两个词 1 和 4。
至于为什么变量不能用在序列表达式中:历史上大括号展开的list形式先出现,后来引入序列表达式形式时,顺序扩展已经修复。
有关大括号扩展的一般概述,请参阅this answer。
解决方法
注意:解决方法侧重于 numerical 序列表达式,如问题中所示;基于eval 的解决方法还演示了使用具有不太常见的字符 序列表达式的变量,这些表达式产生英文字母 的范围(例如,{a..c} 产生@987654342 @)。
seq-based 解决方法是可能的,as demonstrated in Jameson's answer。
一个小警告是seq 不是 POSIX 实用程序,但大多数现代类 Unix 平台都有它。
稍微改进一下,使用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 然后看到 01 a.txt, 02 a.txt, ... 并正确保留了参数边界。
如果您想首先在 Bash array 中稳健地收集结果单词,例如,${words[@]}:
IFS=$'\n' read -d '' -ra words < <(seq -f '%02.f.txt' $var1 $var2)
ls "${words[@]}"
以下是纯 Bash 解决方法:
仅使用 Bash 功能的有限解决方法是使用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。
- 鉴于
eval 的输出必须在此处传递不加引号到ls,以便shell 通过分词将输出拆分为单独的参数,这仅当生成的文件名不包含嵌入的空格或其他 shell 元字符时才有效。
更健壮但更繁琐的纯 Bash 解决方法使用数组来创建等效词:
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[@]}"
- 此方法不存在安全风险,也适用于生成的文件名与嵌入的 shell 元字符(如空格)。
- 可以通过将
i++ 替换为i+=2 来实现自定义增量,例如以2 为增量。
- 实现零填充需要使用
printf;例如,如下:
args+=( "$(printf '%02d.txt' "$i")" ) # -> '01.txt', '02.txt', ...