如何用Bash遍历两个文件并筛选不匹配的value-id组?
问题与解法
文件内容
file1
line1 line2 line3 line4 line5
file2
value: line1 id: 12 -- value: line3 id: 20 -- value: line10 id: 22 -- value: line14 id: 8
需求
找出file2中value:行第二列值不在file1里的对应value行和id行。
期望输出
value: line10 id: 22 value: line14 id: 8
失败代码分析
你写的bash脚本逻辑存在核心问题:
#!/usr/bin/bash for all in "$(cat file1)" do for ids in "$(cat file2 | grep value | awk {'print $2'})" do if [ "$all" != "$ids" ] then echo "$ids" fi done done
- 双引号包裹
$(cat file1)会把整个文件内容当成单个字符串,外层循环仅执行一次,all的值是file1所有行的拼接内容,而非逐行遍历。 - 内层循环同理,
"$(cat file2 | grep value | awk {'print $2'})"会把所有value的第二列拼成空格分隔的字符串,循环也只执行一次。 - 逻辑上不是检查每个
value值是否不在file1中,而是拿整个file1内容和所有value值的拼接串比较,完全不符合需求,且最终仅输出不匹配的字符串,而非对应的完整行。
正确解法
方法一:awk处理(推荐)
awk适合处理这类结构化分段文本,一次遍历即可完成需求:
# 读取file1,将每行存入数组作为合法值集合 NR == FNR { valid_lines[$0] = 1 next } # 遇到分隔符时,检查当前段落的value是否不在合法集合中 /^--$/ { if (current_value && !(current_value in valid_lines)) { print value_line print id_line } # 重置当前段落的变量 current_value = "" value_line = "" id_line = "" next } # 记录value行的完整内容和对应值 /^value:/ { current_value = $2 value_line = $0 next } # 记录id行的完整内容 /^id:/ { id_line = $0 next } # 处理最后一段(file2末尾可能没有--分隔符) END { if (current_value && !(current_value in valid_lines)) { print value_line print id_line } }
保存为script.awk后运行:
awk -f script.awk file1 file2
也可以写成一行命令:
awk 'NR==FNR{valid_lines[$0]=1;next} /^--$/{if(cv&&!(cv in valid_lines)){print vl;print il};cv="";vl="";il="";next} /^value:/{cv=$2;vl=$0;next} /^id:/{il=$0;next} END{if(cv&&!(cv in valid_lines)){print vl;print il}}' file1 file2
方法二:grep+awk组合
先生成匹配file1内容的正则,再筛选file2中不匹配的段落:
# 生成匹配合法value行的正则表达式 valid_pattern=$(printf "^value: (%s)$" "$(paste -d'|' file1)") # 按--分割file2段落,输出不匹配的段落内容 awk -v pat="$valid_pattern" 'BEGIN{RS="--\n";FS="\n"} {if ($1 !~ pat) {print $1; print $2}}' file2 | grep -v '^$'
内容的提问来源于stack exchange,提问作者Saeed
相关产品推荐
相关产品推荐

