在for循环中重击任意glob模式(带空格) [英] Bash arbitrary glob pattern (with spaces) in for loop

查看:133
本文介绍了在for循环中重击任意glob模式(带空格)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有没有办法可靠地使用存储在变量中的任意glob模式?如果模式同时包含空格和元字符,我将遇到困难.这就是我的意思.如果我将模式存储在一个没有空格的变量中,那么看起来一切正常:

Is there any way to reliably use an arbitrary globbing pattern that's stored in a variable? I'm having difficulty if the pattern contains both spaces and metacharacters. Here's what I mean. If I have a pattern stored in a variable without spaces, things seem to work just fine:

<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|

但是,一旦我在模式中添加一个空格,事情就变得站不住脚了:

However, as soon as I throw a space in the pattern, things become untenable:

<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.\?|

很显然,如果我事先知道该模式,则可以手动对其进行转义:

Obviously, if I know the pattern in advance, I can manually escape it:

<prompt> for f in a\ b.* ; do echo "|$f|" ; done
|a b.1|
|a b.2|
|a b.3|

问题是,我正在编写一个脚本,但我事先不知道该模式.有什么方法可以使bash可靠地将变量的内容视为通配模式,而无需采取某种eval欺骗手段?

The problem is, I'm writing a script where I don't know the pattern in advance. Is there any way to reliably make bash treat the contents of a variable as a globbing pattern, without resorting to some sort of eval trickery?

推荐答案

您需要关闭单词拆分功能.回顾一下,这是行不通的:

You need to turn off word-splitting. To recap, this doesn't work:

$ 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是外壳程序的内部字段分隔符".通常将其设置为空格,制表符和换行符.用于变量扩展后的单词拆分.将IFS设置为空将停止单词拆分,从而使该glob起作用.

IFS is the shell's "Internal Field Separator." It is normally set to a space, a tab, and a new line character. It is used for word splitting after variable expansion. Setting IFS to empty stops word splitting and, thereby, allows the glob to work.

这同样适用于数组示例:

The same applies to the array examples:

$ 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分配放在了一个子外壳中.尽管不是必需的,但是这样做的好处是IFS会在子外壳终止时自动返回到其先前的值.如果子外壳不适用于您的应用程序,则这是另一种方法:

Making sure that IFS gets returned to its normal value

In the examples above, I put the IFS assignment inside a subshell. Although not necessary, the advantage of that is that IFS returns automatically to its prior value as soon as the subshell terminates. If subshells are not appropriate for your application, here is another approach:

$ 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

并且,假设我们要匹配该星星.由于我们希望对恒星进行字面上的处理,因此我们必须对其进行转义:

And, suppose that we want to match that star. Since we want the star to be treated literally, we must escape it:

$ p='a\*b.?'
$ ( IFS=; for f in ${p} ; do echo "|$f|" ; done )
|a*b.1|
|a*b.2|
|a*b.3|

由于?不能转义,因此将其视为通配符.因为*被转义,所以它仅与文字*相匹配.

Because the ? is not escaped, it is treated as a wildcard character. Because the * is escaped, it matches only a literal *.

这篇关于在for循环中重击任意glob模式(带空格)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆