通配符在 shell 中扩展,但不在脚本中扩展



所以当我想使用 bash 列出以多个字母开头的文件时,我可以做一些类似 echo /home/username/{A,B,C}* 的事情,它正确地回显以 ABC 开头的文件名。

我正在尝试对脚本中带有用户输入的 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的缺点。

最新更新