Bash中如何通过Glob模式与条件判断排除所有层级嵌套的指定目录?
先直接给你最优解决方案,再一步步拆解你遇到的各种问题:
最优解决方案
要排除任意层级嵌套的venv和ENV_DIR目录(包括它们本身和所有子内容),最简洁高效的方式是直接用find命令的递归遍历和路径过滤能力,完全不需要手动写多层循环。
如果你的文件名没有空格、换行等特殊字符,可以直接用:
find ML*/* -not -path "*/venv" -not -path "*/venv/*" -not -path "*/ENV_DIR" -not -path "*/ENV_DIR/*"
如果需要处理带特殊字符的文件名,用-print0和read -d $'\0'避免解析错误:
find ML*/* -not -path "*/venv" -not -path "*/venv/*" -not -path "*/ENV_DIR" -not -path "*/ENV_DIR/*" -print0 | while read -d $'\0' file; do # 这里替换成你要对文件执行的操作,比如echo或者其他处理逻辑 echo "$file" done
为什么之前的尝试会踩坑?
咱们逐个分析你遇到的问题根源:
1. 为什么if [ "$file" != */venv ]会报错「too many arguments」?
普通的[(对应POSIX标准的test命令)不支持通配符匹配,它只能做精确的字符串对比。当你写*/venv时,Bash会先把这个glob展开成当前目录下所有匹配的文件名,如果有多个匹配项,就会给test命令传递过多参数,直接触发报错。
而Bash的扩展语法[[ ]]支持通配符匹配,所以[[ "$file" != */venv ]]能正常工作,但这只是解决了语法问题,没解决遍历深度的核心问题。
2. 为什么单层/嵌套循环无法处理深层嵌套目录?
你的初始循环for file in ML*/*只能匹配两层路径(比如ML1/subdir、ML2/file.py),更深的路径(比如ML1/subdir/another/venv)根本不会被这个glob匹配到,循环自然处理不到这些内容。
就算你加嵌套循环for innerFile in $file*/*,也只能多覆盖一层,无法应对任意深度的嵌套,显然不是可持续的方案。
3. 为什么之前的find命令换find $file会出现被排除的目录?
你之前写的命令有两个问题:
find的-path参数里,**不是标准通配符(Bash的glob支持**但find默认不支持),所以*/venv/**无法正确匹配venv下的所有子内容,应该用*/venv/*匹配venv的子项,*/venv匹配venv目录本身。- 当你对
file执行find $file时,这是一个独立的find命令,不会继承外层find的过滤条件,会重新遍历该目录下的所有内容,包括之前被排除的路径。其实外层find已经帮你完成了遍历和过滤,直接使用$file即可,不需要再调用find。
补充:如果一定要用Bash循环(不推荐)
如果你因为特殊原因必须用Bash循环而不是find,可以开启Bash的globstar选项,让**匹配任意深度的目录:
shopt -s globstar for file in ML*/**; do if [[ "$file" != */venv && "$file" != */venv/* && "$file" != */ENV_DIR && "$file" != */ENV_DIR/* ]]; then echo "$file" fi done
不过这种方法效率比find低,处理特殊文件名时也容易出问题,所以优先推荐find方案。
备注:内容来源于stack exchange,提问作者Emil11

