You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Bash变量展开引号问题:如何正确传递参数给find命令

解决find命令递归查找C/C++文件的参数引号问题

问题描述

我尝试在脚本中递归查找C语言源文件和头文件,同时避免匹配当前目录下的文件。设置了如下变量:

FILE_MATCH_LIST='"*.c","*.cc","*.cpp","*.h","*.hh","*.hpp"'
FILE_MATCH_REGEX=$(echo "$FILE_MATCH_LIST" | sed 's/,/ -o -name /g')
FILE_MATCH_REGEX="-name $FILE_MATCH_REGEX"

该变量最终生成了符合预期的字符串:

+ FILE_MATCH_REGEX='-name "*.c" -o -name "*.cc" -o -name "*.cpp" -o -name "*.h" -o -name "*.hh" -o -name "*.hpp"'

但调用find命令时出现两种问题:

  1. 给变量加引号调用,整个参数会被单引号包裹,导致find报“unknown predicate”错误:
files=$(find $root_dir "$FILE_MATCH_REGEX" | grep -v $GREP_IGNORE_LIST)

+ find [directory] '-name "*.c" -o -name "*.cc" -o -name "*.cpp" -o -name "*.h" -o -name "*.hh" -o -name "*.hpp"'
  1. 不加引号调用,每个文件名模式会被单引号包裹双引号,导致find查找带引号的字符串而非匹配文件:
files=$(find $root_dir $FILE_MATCH_REGEX | grep -v $GREP_IGNORE_LIST)

+ find [directory] -name '"*.c"' -o -name '"*.cc"' -o -name '"*.cpp"' -o -name '"*.h"' -o -name '"*.hh"' -o -name '"*.hpp"'

这些单引号的来源不明,请问如何解决该引号问题,正确将参数传递给find命令?

解决方案

方法1:使用数组存储find参数(推荐)

Shell数组能正确保留每个参数的边界,不会出现引号解析混乱的问题。修改变量定义为数组:

# 直接定义数组,每个文件模式作为独立元素
FILE_PATTERNS=("*.c" "*.cc" "*.cpp" "*.h" "*.hh" "*.hpp")

# 构建find的参数数组
find_args=()
for pattern in "${FILE_PATTERNS[@]}"; do
    if [ ${#find_args[@]} -eq 0 ]; then
        find_args+=(-name "$pattern")
    else
        find_args+=(-o -name "$pattern")
    fi
done

# 调用find命令,数组展开时必须加双引号
files=$(find "$root_dir" "${find_args[@]}" | grep -v "$GREP_IGNORE_LIST")

这样每个-name和对应的模式都是独立参数,find能完全正确解析。

方法2:去掉变量中的多余引号,结合eval解析(谨慎使用)

修改初始变量,移除不必要的双引号,再生成参数串,最后用eval解析命令:

# 初始列表不带引号
FILE_MATCH_LIST='*.c,*.cc,*.cpp,*.h,*.hh,*.hpp'
# 替换逗号为 -o -name
FILE_MATCH_REGEX=$(echo "$FILE_MATCH_LIST" | sed 's/,/ -o -name /g')
# 拼接前缀
FILE_MATCH_REGEX="-name $FILE_MATCH_REGEX"

# 使用eval重新解析命令字符串(仅当变量内容完全可控时使用,避免注入风险)
files=$(eval find "$root_dir" $FILE_MATCH_REGEX | grep -v "$GREP_IGNORE_LIST")

注意:eval会重新解析整个命令字符串,若变量内容不可控,可能带来安全风险,优先使用数组方法。

原问题原因解析

原变量中包含的双引号是字符串的一部分,当变量展开时,Shell会把这些双引号当成普通字符处理:

  • 加引号展开时,整个字符串被当作一个完整参数,find无法识别这个长串为合法的查询谓词;
  • 不加引号展开时,Shell按空格分割参数,每个"*.c"会被当作一个完整参数传递给-name,导致find去查找文件名包含双引号的文件。

内容的提问来源于stack exchange,提问作者Brydon Gibson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 17:01:21