find -name
我正在尝试在sh
脚本中使用先前计算的条件的复杂论证。简化后,就像
cond="-name '*.txt*"
find . $cond -ls
但现在我遇到的问题是通配符 in 要么$cond
在调用之前由 shell 扩展find
,要么不由find
.
为了测试这一点,我在一个空的测试目录中执行了以下操作:
touch a.txt b.txt c.dat
然后迭代
cond="-name '*.txt'"; echo $cond; find . $cond
具有不同的值$cond
,使用我能想到的所有类型的引用/转义。
我要么得到所有四个条目(包括.
目录),要么没有文件,或者find
抱怨b.txt
自己是一个未知的主要或操作员。当然,正确的输出应该只是两个文本文件。
有什么提示吗?我确信我只是错过了一些基本的东西。
答案1
在类似 Bourne 的 shell 中(除了zsh
),在列表上下文中保留不带引号的变量扩展是分割+全局操作员。在:
cond="-name '*.txt'"; echo $cond
的内容$cond
首先根据$IFS
特殊变量的值进行分割。默认情况下,它位于 ASCII 空格、制表符和换行符上。
所以那是分裂进入-name
和'*.txt'
。然后全局部分被应用。对于每一个字如果包含通配符(此处*
为第二个),则该单词将扩展为与该模式匹配的文件列表,或者如果没有文件匹配,则保持不变。因此,如果没有文件匹配,将扩展到当前目录中名称以或结尾的'*.txt'
文件列表。'
.txt'
'*.txt'
然后,该单词列表作为单独的参数传递给echo
.如果没有匹配的文件,则意味着echo
将收到 3 个参数:"echo"
、"-name"
和"'*.txt'"
。
当然,这同样适用于find
命令。
您希望该find
命令接收参数-name
和*.txt
,因此您需要调整您的分割+全局操作员。
您需要 的值$IFS
来匹配您在 中使用的分隔符$cond
。例如:
cond='-name:*.txt'
IFS=':'
而你不想要全局的一部分分割+全局运算符,因此您需要通过以下方式禁用它:
set -f
所以就变成了:
cond='-name:*.txt'
IFS=:; set -f
find . $cond -ls
或者你可以这样做:
cond='-name *.txt'
set -f
find . $cond -ls
并假设$IFS
其默认值未被修改。
或者,如果您的 shell 支持数组,您可能希望使用它,而不是依赖标量变量并期望 shell 在扩展时将其拆分。
这适用于ksh93
、mksh
、bash
或zsh
:
cond=(-name '*.txt') # an array with two elements: "-name" and "*.txt"
find . "${cond[@]}" -ls # elements of the array expanded as separate arguments
# to find.