Shell 脚本、find -name 和通配符扩展

Shell 脚本、find -name 和通配符扩展

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 在扩展时将其拆分。

这适用于ksh93mkshbashzsh

cond=(-name '*.txt') # an array with two elements: "-name" and "*.txt"
find . "${cond[@]}" -ls # elements of the array expanded as separate arguments
                        # to find.

相关内容