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

时间:2018-08-09 14:24:48

标签: bash

因此,当我想使用bash列出以多个字母开头的文件时,我可以执行类似echo /home/username/{A,B,C}*的操作,该操作正确地回显以AB,{{1 }}。

我正在尝试使用脚本内部用户输入的bash变量来做相同的事情,并说几句(例如,脚本名称为C):
    run_user_input.sh

我以var=$1;echo $var;的身份运行。

但这只是呼应./run_user_input.sh "/home/username/{A,B}*"

请注意,/home/username/{A,B}*仍然可以正常工作。

如何解决?

1 个答案:

答案 0 :(得分:1)

在双引号中,仅扩展了几处内容:最重要的是参数和命令替换,但没有文件名(也没有大括号扩展),因此双引号内的*始终是文字。

然后,在作业的右侧,也没有 文件名和括号扩展,因此var=*会将文字*放入{{1 }},即使没有引号。您的最后一个示例“有效”仅是因为您没有引用var的扩展名,而实际上包含了$var

方法是使用数组:

/home/username/A*

这将同时执行大括号和文件名扩展,并且数组元素将每个包含一个文件名,并正确转义了空格和glob字符。用正确的引号访问它们:

fnames=(/home/username/{A,B,C}*)
例如,

为您提供第一个文件名。

如果将模式作为参数提供给脚本,则可能无法使用echo "${fnames[0]}"

eval

有关pattern=$1 eval fnames=("$pattern") 的{​​{3}} 。如果您将以下内容作为模式提供:

eval

pattern='x); echo pwnd #' 会将行扩展为

eval

并实际运行注入的命令,这可能不仅仅只是回声。

可悲的是,健壮的方法

fnames=(x); echo pwnd #)

不起作用,因为它会阻止扩展。

为避免这种情况,建议您重写脚本以接受多个参数,然后让Shell进行扩展:

eval "$(printf 'fnames=(%q)' "$pattern")"

在脚本中类似

./yourscript path/to/dir/{A,B}*

(与for file; do <something>; done 相同)。现在您可以享受shell扩展带来的好处,而没有for file in "$@"; do <something>; done的缺点了。