find - 通过析取进行通配

find - 通过析取进行通配

我之前编写过一个脚本,用于在目录树中搜索 .h 和 .c 文件,然后对它们运行 clang-format :

find $directory -name '*.[hc]' -exec clang-format -i {} \;

这正如预期的那样工作。现在我想将 .cpp 文件添加到搜索中。然而,两者都没有

-name '*.{[hc],cpp}'

也不

-name '{*.[hc],*.cpp}'

工作。也就是说,他们没有找到任何文件。

find我知道如果我使用's-o选项,我可以让我的逻辑发挥作用。然而,必须有一种方法可以用单个-name指令来做到这一点。

答案1

-name与谓词 in一起使用的模式find是标准文件名通配模式。您尝试使用的是大括号扩展,它find不支持。

请注意,没有单身的.c匹配以,.h或结尾的文件名的标准通配模式.cpp

您可能想要使用类似'*.'{c,h,cpp}, 的内容,它扩展为*.c, *.h, and *.cpp,但不包括-name谓词也不包括-o

接下来要尝试的是'-o -name "*.'{c,h,cpp}'"', 但这扩展到三个字符串-o -name "*.c" -o -name "*.h", 和 -o -name "*.cpp"。这也不能使用,因为您必须将它们拆分为空格才能find识别单独的子字符串(并-o从第一个子字符串中删除 )。虽然它可能会起作用eval,但似乎比它的价值更麻烦。

相反,您可以使用 -name中间用 OR 进行测试:

find  "$directory" -type f \( -name '*.[ch]' -o -name '*.cpp' \) \
    -exec clang-format -i {} +

这使用了-name前面描述的两个测试(-o是 OR 运算符),并且clang-format通过将找到的路径名批量传递给工具而不是为每个文件调用一次来尽可能减少调用次数。

通过一点点额外的编程,您可以将想要在列表中选取的所有文件名后缀存储起来,并find从中创建所需的表达式。

由于您没有提到您正在使用什么 shell,因此我正在为 POSIX shshell 执行此操作:

set -- c h cpp

for suffix do
    set -- "$@" -o -name "*.$suffix"
    shift
done

shift # shifts off the initial "-o"

find "$directory" -type f \( "$@" \) -exec clang-format -i {} +

或者

set --
for suffix in c h cpp; do
    set -- "$@" -o -name "*.$suffix"
done

shift

find "$directory" -type f \( "$@" \) -exec clang-format -i {} +

本例中扩展的列表"$@"相当于

-name '*.c' -o -name '*.h' -o -name '*.cpp'

答案2

-name支持与 shell glob 不同的模式,但它不支持大括号(GNU find 手册页明确提到),也不支持 ksh 样式的扩展 glob。

但很多find人都支持-regex,您也许可以使用它。取决于您find支持的正则表达式方言,即标准的基本正则表达式支持交替。

使用 GNU find,这应该可以工作:

find . -regextype posix-extended -regex '.*\.(c|h|cpp)'

(另请注意,匹配是针对整个路径的匹配,而不仅仅是文件名部分,但如果您仅匹配文件名的最终后缀,那么这不会成为问题。)

相关内容