如何在Bash脚本中实现变量值与glob模式的匹配?
解决Bash脚本中变量存储的Glob模式无法匹配Git变更文件的问题
问题原因
你的代码存在两个核心问题:
- 参数传递时的路径扩展:调用函数时使用
${array[@]}却未加引号,Bash会自动对Glob模式执行路径扩展,把migrations/*.up.sql替换成当前目录下匹配的文件名,导致函数收到的不是模式本身,而是实际文件名。 - 文件名遍历的潜在缺陷:用
for file in $(git diff --name-only ...)遍历文件时,若文件名包含空格或特殊字符,会被错误分割成多个元素。
另外你测试中变量模式不匹配的情况,是因为赋值时未加引号导致Bash提前扩展了Glob模式,变量实际存储的是匹配的文件名而非模式本身。
解决方案
1. 修正函数调用方式
传递数组参数时必须用双引号包裹"${array[@]}",阻止Bash自动扩展Glob模式:
array=("migrations/*.up.sql") files_changed HEAD HEAD~1 "${array[@]}"
2. 修正函数内的文件遍历逻辑
改用while read读取git diff的输出,避免文件名含空格的问题,同时确保[[中$pattern未加引号,让Bash将其当作Glob模式匹配字符串:
function files_changed() { local latter_commit=$1 local former_commit=$2 local files=("${@:3}") # 用while read处理git diff输出,兼容含空格的文件名 git diff --name-only "$former_commit" "$latter_commit" | while IFS= read -r file; do echo "Checking file $file" for pattern in "${files[@]}"; do # $pattern未加引号,Bash会将其作为Glob模式匹配字符串 if [[ "$file" == $pattern ]]; then echo "File $file changed, matched by pattern $pattern" return 0 else echo "File $file did not match pattern $pattern" fi done done echo "No files changed" return 1 }
3. (可选)支持复杂Glob模式
如果需要匹配多级目录(比如**/*.up.sql),可以在脚本开头开启扩展Glob选项:
shopt -s globstar extglob
这样就能正确匹配跨目录的文件模式,且无需依赖当前文件系统中存在对应文件——我们只是用Glob模式匹配Git返回的文件名字符串,而非执行实际路径扩展。
验证测试
执行以下命令验证变量模式匹配逻辑:
X="migrations/20240000000001_migration.up.sql" Y="migrations/*.up.sql" # 确保Y赋值时加引号,避免路径扩展 if [[ "$X" == $Y ]]; then echo "Match"; else echo "No Match"; fi
此时会输出Match,符合预期。
内容的提问来源于stack exchange,提问作者Mattias Martens
相关产品推荐
相关产品推荐

