如何直接对比Bash数组元素的前N个字符并输出匹配项?
问题描述
假设存在两个Bash数组A和B,可通过读取目录下的txt文件生成,或直接声明定义:
通过目录生成数组
- 目录A中执行以下脚本生成数组
A:
#!/bin/bash A=( $(ls *txt) )
目录A包含的文件:
fox_abdce.txt rabbit_abdce.txt lemom_asnrndna.txt
- 目录B中执行以下脚本生成数组
B:
#!/bin/bash B=( $(ls *txt) )
目录B包含的文件:
fox_zzzzzz.txt rabbit_zzzedd.txt lemom_kokoijijim.txt
直接声明数组
也可直接通过代码声明数组:
#!/bin/bash declare -a A=([0]="fox_abcde.txt" [1]="lemom_asnrndna.txt" [2]="rabbit_abcde.txt") declare -a B=([0]="fox_zzzzzz.txt" [1]="lemom_kokoijijim.txt" [2]="rabbit_zzzedd.txt")
需求:对比两个数组的所有元素,判断其前3个字符是否一致,输出所有匹配元素,支持两种格式:
- 格式一(分组列出):
fox_abdce.txt rabbit_abdce.txt lemom_asnrndna.txt fox_zzzzzz.txt rabbit_zzzedd.txt lemom_kokoijijim.txt
- 格式二(对应项并排):
fox_abdce.txt fox_zzzzzz.txt rabbit_abdce.txt rabbit_zzzedd.txt lemom_asnrndna.txt lemom_kokoijijim.txt
此前处理CSV文件两列前N字符匹配的AWK方法如下,需将该逻辑迁移到Bash数组:
设置环境变量:
#!/bin/bash export NUMBER_OF_DIGITS=3
匹配项输出:
awk -F, '{if(substr($1, 1, ENVIRON["NUMBER_OF_DIGITS"]) == substr($2, 1, ENVIRON["NUMBER_OF_DIGITS"])) print}' file.csv
不匹配项输出:
awk -F, '{if(substr($1, 1, ENVIRON["NUMBER_OF_DIGITS"]) != substr($2, 1, ENVIRON["NUMBER_OF_DIGITS"])) print}' file.csv
解决方案
首先统一设置匹配的字符数变量:
export NUMBER_OF_DIGITS=3
实现格式一(分组列出)
遍历数组筛选出前缀匹配的元素,分两组输出:
#!/bin/bash export NUMBER_OF_DIGITS=3 # 替换为目录生成数组的代码也可 declare -a A=([0]="fox_abcde.txt" [1]="lemom_asnrndna.txt" [2]="rabbit_abcde.txt") declare -a B=([0]="fox_zzzzzz.txt" [1]="lemom_kokoijijim.txt" [2]="rabbit_zzzedd.txt") # 输出数组A中所有匹配元素 for elem in "${A[@]}"; do prefix="${elem:0:$NUMBER_OF_DIGITS}" for b_elem in "${B[@]}"; do if [[ "${b_elem:0:$NUMBER_OF_DIGITS}" == "$prefix" ]]; then echo "$elem" break fi done done echo # 空行分隔两组 # 输出数组B中所有匹配元素 for elem in "${B[@]}"; do prefix="${elem:0:$NUMBER_OF_DIGITS}" for a_elem in "${A[@]}"; do if [[ "${a_elem:0:$NUMBER_OF_DIGITS}" == "$prefix" ]]; then echo "$elem" break fi done done
实现格式二(对应项并排)
先为数组B建立前缀到元素的映射,再遍历数组A匹配输出:
#!/bin/bash export NUMBER_OF_DIGITS=3 # 替换为目录生成数组的代码也可 declare -a A=([0]="fox_abcde.txt" [1]="lemom_asnrndna.txt" [2]="rabbit_abcde.txt") declare -a B=([0]="fox_zzzzzz.txt" [1]="lemom_kokoijijim.txt" [2]="rabbit_zzzedd.txt") # 构建数组B的前缀-元素关联映射 declare -A b_prefix_map for elem in "${B[@]}"; do prefix="${elem:0:$NUMBER_OF_DIGITS}" b_prefix_map["$prefix"]="$elem" done # 遍历数组A,匹配后格式化并排输出 for elem in "${A[@]}"; do prefix="${elem:0:$NUMBER_OF_DIGITS}" if [[ -n "${b_prefix_map[$prefix]}" ]]; then # 25为第一列宽度,可根据元素长度调整 printf "%-25s %s\n" "$elem" "${b_prefix_map[$prefix]}" fi done
高效实现(结合AWK)
若数组元素数量多,可将两个数组转为类似CSV的行结构,用AWK批量处理:
#!/bin/bash export NUMBER_OF_DIGITS=3 declare -a A=([0]="fox_abcde.txt" [1]="lemom_asnrndna.txt" [2]="rabbit_abcde.txt") declare -a B=([0]="fox_zzzzzz.txt" [1]="lemom_kokoijijim.txt" [2]="rabbit_zzzedd.txt") # 将两个数组元素按行对齐后传递给AWK匹配 paste <(printf "%s\n" "${A[@]}") <(printf "%s\n" "${B[@]}") | awk -F'\t' '{ if(substr($1, 1, ENVIRON["NUMBER_OF_DIGITS"]) == substr($2, 1, ENVIRON["NUMBER_OF_DIGITS"])) { printf "%-25s %s\n", $1, $2 } }'
注:此方法适合两个数组元素数量相同、或已按前缀排序的场景。
内容的提问来源于stack exchange,提问作者Gabriel G.
相关产品推荐
相关产品推荐

