我有四个文件:
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
什么是正确的代码?
答案 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
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