如何编写封装find命令的辅助函数,统一排除指定目录并支持完整的find参数传入
如何编写封装find命令的辅助函数,统一排除指定目录并支持完整的find参数传入
兄弟,我太懂你这种糟心的情况了——多个脚本里重复写排除目录的find逻辑,时间久了各个脚本的排除列表还不一样,维护起来简直头疼!而且find的参数顺序还特别苛刻,路径必须在表达式前面,想同时兼容自定义路径和表达式确实有点绕不过来。不过别慌,咱们用几个shell小技巧就能搞定这个my_find辅助函数,既能统一排除危险目录,又能像原生find一样支持所有参数!
核心思路拆解
要实现这个需求,我们需要解决两个关键问题:
- 正确分离用户传入的路径参数和表达式参数,确保排除目录的逻辑插入到合适的位置
- 用
find原生的-prune选项跳过危险目录(比事后过滤效率高得多,find会直接跳过这些目录的遍历) - 自动处理默认路径,同时避免重复输出(比如用户已经指定
-print0或-exec时,不再额外加-print)
完整的辅助函数代码
my_find() { # 定义需要排除的危险目录模式(相对于find的起始路径) local hazard_conditions="\ -path '_output' \ -o -path 'release' \ -o -path 'target' \ -o -path '*/third_party/*' \ -o -path '*/vendor/*' \ " # 分离用户传入的路径参数和表达式参数 local paths=() local exprs=() local in_expr=false for arg in "$@"; do if [[ "$arg" == -* ]]; then # 遇到以-开头的参数,开始处理表达式部分 in_expr=true exprs+=("$arg") else if $in_expr; then # 已经进入表达式阶段,后续非-开头的参数是表达式的参数(比如-exec的命令) exprs+=("$arg") else # 还在路径阶段,加入路径列表 paths+=("$arg") fi fi done # 如果用户没传路径,默认使用当前目录 if [ ${#paths[@]} -eq 0 ]; then paths=(.) fi # 判断用户是否已经指定了输出/动作类参数(避免重复输出) local has_action=false for e in "${exprs[@]}"; do case "$e" in -print|-print0|-exec|-ok|-delete|-ls|-fls) has_action=true break ;; esac done # 执行最终的find命令 if $has_action; then find "${paths[@]}" \( $hazard_conditions \) -prune -o "${exprs[@]}" else find "${paths[@]}" \( $hazard_conditions \) -prune -o "${exprs[@]}" -print fi }
实际使用示例
默认路径+表达式过滤:
# 在当前目录下查找所有最近7天修改的.sh脚本,自动跳过危险目录 my_find -type f -name '*.sh' -mtime -7指定多路径+动作命令:
# 在./src和./tests目录下,把所有.md文件复制到./backup/,自动跳过危险目录 my_find ./src ./tests -name '*.md' -exec cp {} ./backup/ \;带特殊输出格式:
# 查找当前目录下的所有.go文件,用null分隔输出(适合xargs -0) my_find -type f -name '*.go' -print0
注意事项
- 如果需要调整危险目录列表,直接修改函数里的
hazard_conditions即可,所有使用my_find的脚本都会自动同步更新,再也不会出现列表不一致的问题 - 用
-path而不是-wholename是为了保证不管用户指定什么起始路径,排除规则都能生效(比如用户指定./src,-path '_output'会匹配./src/_output)
备注:内容来源于stack exchange,提问作者Tim Hockin
相关产品推荐
相关产品推荐

