所以当我想使用 bash 列出以多个字母开头的文件时,我可以做一些类似 echo /home/username/{A,B,C}*
的事情,它正确地回显以 A
、B
、C
开头的文件名。
我正在尝试对脚本中带有用户输入的 bash 变量做同样的事情,说一些类似的话(假设脚本名称为 run_user_input.sh
(:
var=$1;echo $var;
我按照./run_user_input.sh "/home/username/{A,B}*"
运行它.
但这只是呼应/home/username/{A,B}*
.
请注意,var="/home/username/A*";echo $var;
仍然可以正常工作。
如何解决这个问题?
在双引号中,只有几件事被扩展:参数和命令替换,最重要的是,但没有文件名(也没有大括号扩展(,所以双引号内的*
总是文字。
然后,在作业的右侧,也没有文件名和大括号扩展,因此即使没有引号,var=*
也会将文字*
放入var
中。您的最后一个示例"有效"只是因为您没有引用$var
的扩展,但它实际上包含/home/username/A*
.
这样做的方法是使用数组:
fnames=(/home/username/{A,B,C}*)
这将执行大括号和文件名扩展,并且数组元素将分别包含一个文件名,正确转义空格和 glob 字符。通过适当的引用访问它们:
echo "${fnames[0]}"
例如,为您提供第一个文件名。
如果将模式作为参数提供给脚本,则可能无法使用 eval
:
pattern=$1
eval fnames=("$pattern")
这带有通常的警告 关于eval
.如果提供以下模式:
pattern='x); echo pwnd #'
eval
将生产线扩展到
fnames=(x); echo pwnd #)
并实际运行注入的命令,这可能不如回显友好。
可悲的是,稳健的方法
eval "$(printf 'fnames=(%q)' "$pattern")"
不起作用,因为它会阻止扩展。
为了避免这种情况,我建议重写脚本以接受多个参数并让 shell 进行扩展:
./yourscript path/to/dir/{A,B}*
在脚本中类似
for file; do <something>; done
(与for file in "$@"; do <something>; done
相同(。现在,您可以享受由外壳处理扩展的好处,而没有eval
的缺点。