有没有办法可靠地使用存储在变量中的任意globbing模式?如果模式包含空格和元字符,我会遇到困难。这就是我的意思。如果我将一个模式存储在一个没有空格的变量中,那么事情似乎就可以了:
<prompt> touch aa.{1,2,3} "a b".{1,2,3}
<prompt> p="aa.?"
<prompt> for f in ${p} ; do echo "|$f|" ; done
|aa.1|
|aa.2|
|aa.3|
<prompt> declare -a A=($p) ; for f in "${A[@]}" ; do echo "|$f|" ; done
|aa.1|
|aa.2|
|aa.3|
然而,只要我在模式中抛出一个空格,事情就变得站不住脚了:
<prompt> p="a b.?"
<prompt> for f in ${p} ; do echo "|$f|" ; done
|a|
|b.?|
<prompt> declare -a A=($p) ; for f in "${A[@]}" ; do echo "|$f|" ; done
|a|
|b.?|
<prompt> for f in "${p}" ; do echo "|$f|" ; done
|a b.?|
<prompt> for f in $(printf "%q" "$p") ; do echo "|$f|" ; done
|a\|
|b.\?|
显然,如果事先知道模式,我可以手动逃避它:
<prompt> for f in a\ b.* ; do echo "|$f|" ; done
|a b.1|
|a b.2|
|a b.3|
问题是,我正在编写一个我不提前知道模式的脚本。有没有办法可靠地使bash将变量的内容视为通配模式,而不采用某种eval
技巧?
答案 0 :(得分:6)
您需要关闭分词功能。回顾一下,这不起作用:
$ p="a b.?"
$ for f in ${p} ; do echo "|$f|" ; done
|a|
|b.?|
然而,这样做:
$ ( IFS=; for f in ${p} ; do echo "|$f|" ; done )
|a b.1|
|a b.2|
|a b.3|
IFS
是shell的“内部字段分隔符”。它通常设置为空格,制表符和换行符。它用于变量扩展后的单词拆分。将IFS
设置为空会停止分词,从而允许glob工作。
同样适用于数组示例:
$ declare -a A=($p) ; for f in "${A[@]}" ; do echo "|$f|" ; done
|a|
|b.?|
$ ( IFS=; declare -a A=($p) ; for f in "${A[@]}" ; do echo "|$f|" ; done )
|a b.1|
|a b.2|
|a b.3|
IFS
恢复正常值在上面的示例中,我将IFS
赋值放在子shell中。虽然没有必要,但优点是IFS
在子shell终止后自动返回其先前值。如果子套不适合您的应用,这是另一种方法:
$ oldIFS=$IFS; IFS=; for f in ${p} ; do echo "|$f|" ; done; IFS=$oldIFS
|a b.1|
|a b.2|
|a b.3|
假设我们的名称中包含文字*
的文件:
$ touch ab.{1,2,3} 'a*b'.{1,2,3}
$ ls
a*b.1 ab.1 a*b.2 ab.2 a*b.3 ab.3
并且,假设我们想要匹配那个明星。既然我们希望明星得到真正的对待,我们必须逃避它:
$ p='a\*b.?'
$ ( IFS=; for f in ${p} ; do echo "|$f|" ; done )
|a*b.1|
|a*b.2|
|a*b.3|
由于?
未转义,因此将其视为通配符。由于*
已转义,因此它仅匹配文字*
。
答案 1 :(得分:0)
中使用的模式
p="a b.?"
不正确,如果您直接使用它就很清楚了:
A=( a b.? )
正如问题中所述,正确的模式是
a\ b.?
所以正确的变量赋值是
p='a\ b.?'
模式来自哪里?他们可以在源头纠正吗?对于 例如,如果通过添加'。?'来创建模式。到基地,你可以 使用'printf'进行所需的引用:
base='a b'
printf -v p '%q.?' "$base"
'set'然后显示:
p='a\ b.?'
不幸的是,如果你尝试
,单词拆分仍会导致失败A=( $p )
'set'显示:
A=([0]="a\\" [1]="b.?")
解决此问题的一种方法是使用'eval':
eval "A=( $p )"
'set'然后显示:
A=([0]="a b.1" [1]="a b.2" [2]="a b.3")
这是“eval trickery”,但它并不比IFS技巧更糟糕 先前描述过。此外,如果文件是IFS技巧将无济于事 匹配在他们的名字中有globbing元字符。你做了什么,为 例如,如果您已经使用
创建了文件touch ab.{1,2,3} 'a*b'.{1,2,3}
你想匹配基数是'a * b'的那些?没有IFS的数量 如果有的话,欺骗将使得与变量p正确匹配成为可能 设置为
p="a*b.?"
在
base='a*b'
printf -v p '%q.?' "$base"
'set'显示:
p='a\*b.?'
之后
eval "A=( $p )"
它显示:
A=([0]="a*b.1" [1]="a*b.2" [2]="a*b.3")
我认为使用'eval'是最后的手段,但在这种情况下我无法思考 更好的选择,这是非常安全的。