Shell脚本对比存储多行SQL的数组以输出A.sql独有内容的问题排查
原脚本存在的核心问题
- 读取B.sql的语法错误:拆分B.sql的循环中,
read -rd ';'second_sql缺少分隔符参数与变量名之间的空格,shell 会把;second_sql整体作为-d的自定义分隔符,完全无法按分号拆分B.sql内容,导致存储B.sql的数组内容完全失真。这也是你运行脚本时只输出第一条SQL的核心原因。 read -d模式的内容丢失风险:read只有碰到指定分隔符时才会返回成功退出码,如果文件最后一段SQL结尾没有分号,或是读到文件EOF仍未匹配到分隔符,read 会返回非零值,对应SQL块会被直接丢弃,无法存入数组。- 变量引用不规范:匹配判断语句
[[ $p == $q ]]未给变量加双引号,当SQL内容包含通配符(比如SQL里的*)、特殊字符时,会被shell解析成通配匹配规则,导致判断结果错误。 - 对比逻辑完全错乱:
- 匹配成功时的提示文本写错,将"SQL存在于B数组"的判断逻辑误写为"属于first_array"
- 没有基于flag的分支判断,不管SQL是否在B数组中存在,都会执行后续的"不存在"输出,还夹杂了多余的调试输出(
echo $flag) - 缺少核心分支:只有当flag为false(即B数组中找不到匹配项)时,才需要输出A中独有的内容,原脚本完全没有实现这个判断逻辑。
- SQL内容未做规范化处理:因为单条SQL跨多行存储,拆分后的内容会自带前后换行、缩进、行内连续空白,哪怕两条SQL语义完全一致,只要空白、换行的位置不同就会被判定为不匹配,产生大量误判。
脚本修改方案
针对上述问题做对应调整:
- 修正所有语法空格问题,给read参数、变量引用加上正确的空格与双引号
- 给每个拆分出的SQL块做规范化处理:用
tr把跨行换行、连续缩进/空格统一替换为单个空格,去掉内容前后的多余空白,消除格式差异导致的匹配失败 - 专门处理文件末尾无分号的场景:即使read因为读到EOF返回非零,只要存在有效未存储的SQL块,就将其存入对应数组
- 理顺对比逻辑:内层循环找到匹配项时将flag置为true,内层循环全部结束后,仅当flag为false时,才输出A中独有的SQL内容,自动去掉多余的调试输出和错误提示
- 输出内容时补回原SQL结尾的分号,保证输出格式和原始SQL一致
修改后的完整可运行脚本如下:
#!/bin/bash # 初始化存储数组 first_array=() second_array=() # 读取A.sql,按分号拆分SQL i=0 while IFS= read -rd ';' first_sql; do # 规范化SQL:统一空白格式,清理前后无效字符 normalized=$(echo "$first_sql" | tr -s '[:space:]' ' ' | sed 's/^ *//;s/ *$//') # 跳过空内容 [[ -n $normalized ]] && first_array[$i]="$normalized" && i=$((i+1)) done < A.sql # 处理A.sql末尾无分号的最后一段内容 if [[ -n $first_sql ]]; then normalized=$(echo "$first_sql" | tr -s '[:space:]' ' ' | sed 's/^ *//;s/ *$//') [[ -n $normalized ]] && first_array[$i]="$normalized" fi # 读取B.sql,按分号拆分SQL j=0 while IFS= read -rd ';' second_sql; do normalized=$(echo "$second_sql" | tr -s '[:space:]' ' ' | sed 's/^ *//;s/ *$//') [[ -n $normalized ]] && second_array[$j]="$normalized" && j=$((j+1)) done < B.sql # 处理B.sql末尾无分号的最后一段内容 if [[ -n $second_sql ]]; then normalized=$(echo "$second_sql" | tr -s '[:space:]' ' ' | sed 's/^ *//;s/ *$//') [[ -n $normalized ]] && second_array[$j]="$normalized" fi # 对比两个数组,输出A有但B没有的SQL for p in "${first_array[@]}"; do is_exist=false for q in "${second_array[@]}"; do if [[ $p == "$q" ]]; then is_exist=true break fi done # 仅当B中无匹配时,输出差异内容 if [[ $is_exist == false ]]; then echo "$p;" fi done
效果说明
- 针对你提供的示例场景:A.sql包含4条SQL、B.sql包含3条SQL,脚本会正确输出
Select * from subject;,和预期完全一致 - 完全支持跨多行的长SQL拆分匹配,不会因为单条SQL跨4-5行、存在缩进/换行差异就误判
- 兼容文件末尾SQL无分号的边界场景,不会丢失最后一段SQL内容
- 自动忽略SQL之间的空白、换行格式差异,只要SQL语义一致就会判定为匹配
内容的提问来源于stack exchange,提问作者user14842503
相关产品推荐
相关产品推荐

