Arrays find命令的参数展开
考虑代码(变量Arrays find命令的参数展开,arrays,bash,find,variable-expansion,Arrays,Bash,Find,Variable Expansion,考虑代码(变量$i之所以存在,是因为它在一个循环中,为模式添加了几个条件,例如*.a和*.b,…但为了说明这个问题,只有一个通配符模式就足够了): 如果在包含文件bar和foo.a的文件夹上运行,它将工作,输出: ./foo.a ./bar 但是,如果您现在向文件夹中添加一个新文件,即zoo.a,则它将不再工作: find: paths must precede expression: zoo.a 大概是因为*.$i中的通配符被shell扩展为foo.a zoo.a,从而导致无效的find命
$i
之所以存在,是因为它在一个循环中,为模式添加了几个条件,例如*.a
和*.b
,…但为了说明这个问题,只有一个通配符模式就足够了):
如果在包含文件bar
和foo.a
的文件夹上运行,它将工作,输出:
./foo.a
./bar
但是,如果您现在向文件夹中添加一个新文件,即zoo.a
,则它将不再工作:
find: paths must precede expression: zoo.a
大概是因为*.$i
中的通配符被shell扩展为foo.a zoo.a
,从而导致无效的find
命令模式。因此,一种修复方法是在通配符模式周围加引号。除非它不起作用:
- 使用单引号--
时,PATTERN=“-name bar-或-name'*.$i'
命令仅输出find
。转义单引号(bar
)会产生相同的结果\'
- 带双引号的idem:
——仅返回PATTERN=“-name bar-或-name\”*.$i\”
bar
- 在
命令中,如果将find
替换为$PATTERN
,则会出现一个错误(对于单引号,错误相同,但在通配符模式周围有单引号): 查找:未知谓词“$PATTERN”
-名称栏-或-name“*.a”
$PATTERN
替换为'$PATTERN'
也不起作用。。。(没有发生任何扩张)
我能让它工作的唯一方法就是使用<代码>评估
FINDSTR="find . \( $PATTERN \)"
eval $FINDSTR
这可以正常工作:
./zoo.a
./foo.a
./bar
在谷歌搜索了很多次之后,我看到它多次提到要做这种事情,应该使用数组。但这不起作用:
i="a"
PATTERN=( -name bar -or -name '*.$i' )
find . \( "${PATTERN[@]}" \)
# result: ./bar
在find
行中,数组必须用双引号括起来,因为我们希望将其展开。但通配符表达式周围的单引号不起作用,也根本不使用引号:
i="a"
PATTERN=( -name bar -or -name *.$i )
find . \( "${PATTERN[@]}" \)
# result: find: paths must precede expression: zoo.a
但双引号确实有效
i="a"
PATTERN=( -name bar -or -name "*.$i" )
find . \( "${PATTERN[@]}" \)
# result:
# ./zoo.a
# ./foo.a
# ./bar
所以我想我的问题实际上是两个问题:
a) 在最后一个使用数组的示例中,为什么*.$i
周围需要双引号
b) 以这种方式使用数组应该是扩展的。如何使用变量实现这一点(参见我的第一次尝试)?在实现这个功能后,我返回并再次尝试使用一个变量,使用黑色斜杠单引号,或\\\'
,但没有任何效果(我只得到条
)。我需要做什么才能模仿“手工”使用数组时的引用
提前感谢您的帮助。必读书目:
- -
*.$i
周围需要双引号
您需要使用某种形式的引号来防止shell在*
上执行全局扩展。变量不以单引号展开,因此“*。$i”
不起作用。它确实抑制glob扩展,但也阻止变量扩展<代码>“*。$i”禁止全局扩展,但允许变量扩展,这是完美的
要真正深入了解细节,您需要在这里做两件事:
*
以防止全局扩展$i
视为变量展开,但引用它以防止分词和全局展开\*
、“*”
、“*”
、“*”
和$“*”
都是可以接受的方式,以确保它被视为文字星号
对于第2项,双引号是唯一的答案。裸露的$i
会受到分词和全局搜索的影响——如果您有i='foo bar'
或i='foo*'
,则空格和全局搜索将导致问题\$i
和'$i'
都按字面意思处理美元符号,因此它们都不存在
“$i”
是唯一正确的引用。这就是为什么常见的shell建议总是双引号变量展开
最终结果是,以下任何一项都会起作用:
"*.$i"
\*."$i"
'*'."$i"
"*"."$i"
'*.'"$i"
显然,第一个是最简单的
b) 以这种方式使用数组应该扩展«到单独引用的所有元素»。如何使用变量实现这一点(参见我的第一次尝试)?在实现这个功能后,我返回并再次尝试使用一个变量,使用黑色斜杠单引号,或\\\'
,但没有任何效果(我只得到条
)。我需要做什么才能模仿“手工”使用数组时的引用
你必须用eval
拼凑一些东西,但那很危险。从根本上说,数组比简单的字符串变量更强大。没有引号和反斜杠的神奇组合可以让你做数组能做的事情。数组是执行此任务的正确工具
你能更详细地解释一下为什么PATTERN=“-name bar-或-name\”*.$i\”
不起作用?当实际运行find
命令时,带引号的双引号应展开$i
,但不展开glob
当然。假设我们写:
i=a
PATTERN="-name bar -or -name \"*.$i\""
find . \( $PATTERN \)
前两行运行后,$PATTERN
的值是多少?让我们检查一下:
$ i=a
$ PATTERN="-name bar -or -name \"*.$i\""
$ printf '%s\n' "$PATTERN"
-name bar -or -name "*.a"
您会注意到,$i
已被a
替换,反斜杠已被删除
现在让我们看看find
命令是如何解析的。在最后一行中,$PATTERN
是不带引号的,因为我们希望所有单词都分开,对吗?如果编写一个裸变量名,Bash将执行一个隐含的split+glob操作。它执行分词和全局扩展。这到底是什么意思
让我们看看Bash如何执行命令行扩展。在“扩展”部分下,我们可以看到操作顺序:
$ i=a
$ PATTERN="-name bar -or -name \"*.$i\""
$ printf '%s\n' "$PATTERN"
-name bar -or -name "*.a"
['find . \( $PATTERN \)']
['find', '.', '\(', '$PATTERN', '\)']
['find', '.', '\(', '-name bar -or -name "*.a"', '\)']
['find', '.', '\(', '-name', 'bar', '-or', '-name', '"*.a"', '\)']
['find', '.', '\(', '-name', 'bar', '-or', '-name', '"*.a"', '\)']
['find', '.', '(', '-name', 'bar', '-or', '-name', '"*.a"', ')']
$ touch 'foobar' '"foobar"'
$ ls
foobar "foobar"
$ ls foo*
foobar
$ ls "foo*"
ls: foo*: No such file or directory
$ var="\"foo*\""
$ echo "$var"
"foo*"
$ ls $var
"foobar"