我正在尝试使用一些变量来调用 find 。到目前为止我有这个:
DIRECTORY="./ "
FILENAME=-regex" .*test.*"
find $DIRECTORY $FILENAME
效果很好。如果我将文件名更改为:
FILENAME=-regex" .*"
要列出目录中的所有文件,我收到错误:
find: paths must precede expression: ..
我究竟做错了什么?
答案1
变量的内容FILENAME
是
-regex .*test.*
引号不是变量值的一部分,而是 shell 语法的一部分。 shell 解析了该行,解释了引号,甚至在它发现这是一个赋值之前。
当您运行该行时find $DIRECTORY $FILENAME
,$FILENAME
首先被其值替换。然后 shell 对变量的值执行两个扩展步骤:它将它分成单独的单词,其中变量包含空格(您恰好想要),并对每个单词执行通配符(即通配符扩展),因此.*
扩展为当前目录中以点开头的文件名列表。
您的第一个代码片段没有遇到问题,因为该模式.*test.*
恰好与任何文件不匹配,因此它扩展到自身。
当您需要存储多个单词时,可靠的解决方案是使用数组变量而不是字符串变量。
DIRECTORY="./"
EXPRESSION=("-regex" ".*test.*")
find "$DIRECTORY" "${EXPRESSION[@]}"
或者如果您想允许多个目录:
DIRECTORIES=("./")
EXPRESSION=("-regex" ".*test.*")
find "${DIRECTORIES[@]}" "${EXPRESSION[@]}"
"${EXPRESSION[@]}"
扩展为数组中的元素列表(以单独的单词表示)。
"$foo"
请注意,您应该始终在变量替换和命令替换周围使用双引号"$(foo)"
,除非您知道希望值发生分词和通配符。
在这里,由于您使用的是 bash,因此数组是正确的解决方案。在没有数组的 shell 中,有多种可能性。您可以使用位置参数,它们实际上是一个数组变量,但只有当您不将它们用于其他用途时才有可能:
set -- "./" # start with the directory
set -- "$@" -regex '.*' # add the expression
find "$@" # go
另一种方法是正确引用变量的内容,为以后的分词和通配符做好准备。您可以在通配符 ( ?*[
) 之前使用反斜杠以使其按字面解释。
DIRECTORY=./
EXPRESSION='-regex .\*'
find "$DIRECTORY" $EXPRESSION
另一种方法是禁用通配符set -f
。在执行该find
行之前。set +f
如果之后需要,请务必将其重新打开。请注意,由于您使用分词,因此您将无法将空格字符作为谓词参数的一部分find
。
DIRECTORY=./
EXPRESSION='-regex .*'
set -f
find "$DIRECTORY" $EXPRESSION
另请参阅我试图将命令放入变量中,但复杂的情况总是失败!在 bash 常见问题解答中。
答案2
任何一个
directory=.
match=(-regex '.*test.*')
find "$directory" "${match[@]}"
或者(这里使用“,”只是为了表明观点):
directory=.
match='-regex,.*test.*'
IFS=,
set -f
find "$directory" $match
或者:
directory=.
match="-regex '.*test.*'"
eval 'find "$directory" '"$match"
答案3
您没有引用变量。因此,shell 看到的是
find ./ -regex .*
并且它扩展.*
到. ..
.