解决find命令行添加排除项时的通配符展开报错问题
解决find命令排除项通配符被Shell提前展开的问题
这个坑我之前也踩过!问题的核心在于Shell的通配符展开时机:当你通过$(gen.exclude "$@")把生成的字符串传递给find时,Shell会先把*/a/*这种通配符展开成当前目录下匹配的实际路径(比如你的例子里会展开成b/a/),这就把find原本需要的通配符参数给拆成了多个无效参数,自然报错。而如果直接给通配符加引号,引号会被当成-path参数的一部分,find会去匹配带引号的路径,肯定失效。
最可靠的解决办法是用Bash数组来存储和传递find的参数——数组能完美保留每个参数的独立性,不会让Shell提前解析通配符。下面是修改后的完整脚本:
#!/usr/bin/env bash gen.exclude () { local i # 初始化空数组来存排除规则的参数 local exclude_params=() for i in "$@"; do # 把每个排除规则拆成两个独立参数加入数组:-not 和 -path "*${i}*" exclude_params+=(-not -path "*${i}*") done # 用%q转义参数,确保传递时不会被Shell误解析 printf '%q ' "${exclude_params[@]}" } run.find () { # 构建find的完整参数数组 local find_params=(. $(gen.exclude "$@") -print) # 打印命令方便调试 echo "${find_params[*]}" # 用数组展开的方式调用find,每个参数都能正确传递 find "${find_params[@]}" } # 测试代码部分不变 rm -rf a b mkdir a b echo "file 1a" >a/file1 echo "file 2a" >a/file2 echo "file 3a" >a/file3 echo "file 1b" >b/file1 echo "file 2b" >b/file2 echo "file 3b" >b/file3 cp -r "a" "b/a" run.find '/a/'
核心改进说明:
- 数组存储参数:
exclude_params数组把-not和-path "*${i}*"作为独立元素保存,彻底避免了Shell对通配符的提前展开。 printf '%q'转义输出:确保生成的参数在传递过程中不会被Shell意外解析,比如空格、通配符这些特殊字符都能安全传递。- 数组展开调用find:
find "${find_params[@]}"会把数组的每个元素作为单独的参数传给find,完全符合find命令的参数要求。
如果觉得gen.exclude函数有点冗余,还可以直接在run.find里构建参数数组,写法更简洁:
run.find () { # 先初始化基础参数:当前目录和-print local find_params=(. -print) # 把每个排除项插入到-print前面 for i in "$@"; do find_params=("${find_params[@]::${#find_params[@]}-1}" -not -path "*${i}*" "${find_params[@]: -1}") done echo "${find_params[*]}" find "${find_params[@]}" }
这种写法完全避开了子shell生成字符串的环节,直接用数组管理参数,是Bash中处理复杂命令参数的标准实践,再也不用担心通配符展开的问题了。
内容的提问来源于stack exchange,提问作者Craig
相关产品推荐
相关产品推荐

