shell 辅助函数包装“find”并忽略一些目录

shell 辅助函数包装“find”并忽略一些目录

我有一堆遵循以下模式的脚本:

  • 查找与模式匹配的文件
  • 做一点事

问题是,有几个“危险”目录又大又慢,几乎每个脚本都应该避免。它们中的许多目录会做以下事情:

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考虑一下:

  1. 在 的调用中find,首先可能有选项,参数以 开头-。可移植选项是-H-L。实现可能会引入其他选项。

  2. 然后是路径。有些实现find在路径数为零时工作良好(例如 GNU默认find使用零)。.

  3. 最后,以 开头的参数(如果有)-,或者是!(是表达式的开头,该表达式由原语和运算符组成。可能没有这样的参数,表达式可能为空。实现可能支持不可移植的原语和/或运算符。

这意味着find -X …可能是一个有效的命令,可以与某些(未来、自定义)的实现一起使用find,但我们无法提前知道-X是选项还是表达式的开头。除非您知道这find将如何处理它,否则无法判断。我们的简单函数不会尝试猜测,用户应该知道并使用my_find -X -exclude_hazards …-X是选项)或my_find -exclude_hazards -X …-X是表达式的开头)。

相关内容