我有一堆遵循以下模式的脚本:
- 查找与模式匹配的文件
- 做一点事
问题是,有几个“危险”目录又大又慢,几乎每个脚本都应该避免。它们中的许多目录会做以下事情:
find_files() {
find . -not \( \
\( \
-wholename './_output' \
-o -wholename './release' \
-o -wholename './target' \
-o -wholename '*/third_party/*' \
-o -wholename '*/vendor/*' \
\) -prune \
\)
}
我遇到的第一个问题是,每个脚本都嵌入了危险列表,但随着时间的推移,它们已经发生了变化。我想我会编写一个辅助函数,它感觉像 find,但包含一些覆盖。
现在第二个问题:其中一些需要参数化路径,所以:
find_files() {
find "$@" -not \( \
有些需要通过在末尾粘贴“$@”来参数化表达式。
由于 find 的工作方式,没有脚本可以同时参数化路径和表达式——路径必须位于表达式之前,但表达式评估更为复杂,我不知道如何在最后添加过滤器。
在 find 中是否存在一些我无法弄清楚的技术(我承认 find 的行为对我来说很奇怪)可以让我写出类似这样的内容:
my_find() {
# some find expression which takes "$@" as valid args to find
# and ignores all the hazard directories
}
my_find . -type f -name '*.foobar' -print0
?
答案1
以下函数将把每个参数精确转换-exclude_hazards
为负责所需排除的参数:
my_find () (
#!/bin/sh
mark=
for arg do
[ -z "$mark" ] && { set --; mark=y; }
case "$arg" in
-exclude_hazards )
set -- "$@" \
! \( \
\( \
-path './_output' \
-o -path './release' \
-o -path './target' \
-o -path '*/third_party/*' \
-o -path '*/vendor/*' \
\) -prune \
\)
;;
* )
set -- "$@" "$arg"
;;
esac
done
exec find "$@"
)
用法:
my_find . -exclude_hazards -type f -name '*.foobar' -print0
这并非您想要的,但实现很简单,无需了解其他参数的含义。换句话说,函数不必解析其他参数并将它们归类为选项/路径/表达式即可注入额外代码*。您明确地将其放在正确-exclude_hazards
的位置,这样就不会产生歧义、猜测,也不会依赖某些特定的实现find
。吻!
注意my_find . -exclude_hazards -exec echo -exclude_hazards \;
将转换 的两次出现-exclude_hazards
。我可以让函数只转换第一次出现,但由于我不认为这个怪癖在实践中会成为问题,所以我决定不让代码复杂化。此外,修复函数仍然会对 产生不良影响my_find . -exec echo -exclude_hazards \;
。在我看来,这是 KISS 的合理价格。
笔记:
该 shell 代码是可移植的。
我希望该解决方案能够与几乎任何实现兼容
find
,因此- 我将您的非便携式设备更改
-not
为等效便携式设备!
; - 我将您的非便携式设备更改
-wholename
为等效便携式设备-path
。
- 我将您的非便携式设备更改
看起来像 shebang 的只是一条注释,在创建函数时它将被忽略。我把它放在那里,这样您就可以复制整个函数主体,粘贴到常规文件中,并使该文件成为独立脚本;然后该行将变成 shebang,可能不需要进行任何调整。
* 只有当您确切知道您的参数是什么时,才可以将参数分类为选项/路径/表达式。find
考虑一下:
在 的调用中
find
,首先可能有选项,参数以 开头-
。可移植选项是-H
和-L
。实现可能会引入其他选项。然后是路径。有些实现
find
在路径数为零时工作良好(例如 GNU默认find
使用零)。.
最后,以 开头的参数(如果有)
-
,或者是!
或(
是表达式的开头,该表达式由原语和运算符组成。可能没有这样的参数,表达式可能为空。实现可能支持不可移植的原语和/或运算符。
这意味着find -X …
可能是一个有效的命令,可以与某些(未来、自定义)的实现一起使用find
,但我们无法提前知道-X
是选项还是表达式的开头。除非您知道这find
将如何处理它,否则无法判断。我们的简单函数不会尝试猜测,用户应该知道并使用my_find -X -exclude_hazards …
(-X
是选项)或my_find -exclude_hazards -X …
(-X
是表达式的开头)。