Jos*_*lts 6 bash glob variable-expansion
有没有办法可靠地使用存储在变量中的任意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|
Run Code Online (Sandbox Code Playgroud)
但是,只要我在模式中抛出一个空格,事情就变得站不住脚了:
<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.\?|
Run Code Online (Sandbox Code Playgroud)
显然,如果我事先知道模式,我可以手动逃避它:
<prompt> for f in a\ b.* ; do echo "|$f|" ; done
|a b.1|
|a b.2|
|a b.3|
Run Code Online (Sandbox Code Playgroud)
问题是,我正在写一个脚本,我不提前知道这个模式.有没有办法可靠地使bash将变量的内容视为一个通配模式,而不是诉诸某种eval
欺骗?
你需要关闭分词.回顾一下,这不起作用:
$ p="a b.?"
$ for f in ${p} ; do echo "|$f|" ; done
|a|
|b.?|
Run Code Online (Sandbox Code Playgroud)
但是,这样做:
$ ( IFS=; for f in ${p} ; do echo "|$f|" ; done )
|a b.1|
|a b.2|
|a b.3|
Run Code Online (Sandbox Code Playgroud)
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|
Run Code Online (Sandbox Code Playgroud)
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|
Run Code Online (Sandbox Code Playgroud)
假设我们的文件*
名称中包含文字:
$ 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
Run Code Online (Sandbox Code Playgroud)
而且,假设我们想匹配那颗星.既然我们希望明星得到真正的对待,我们必须逃避它:
$ p='a\*b.?'
$ ( IFS=; for f in ${p} ; do echo "|$f|" ; done )
|a*b.1|
|a*b.2|
|a*b.3|
Run Code Online (Sandbox Code Playgroud)
由于?
未进行转义,因此将其视为通配符.因为它*
是转义的,所以它只匹配一个文字*
.