如何用Bash遍历两个文件匹配ID后删除对应行及上一行
问题描述
现有两个文件file1和file2:
file1内容为若干符合格式[0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}的ID:
8dh4rfvj-39fj-gje3-4tgf-2948l12356vz Pdh4rfvj-39fj-gje3-4tgf-2948l12v56vz 9dh5hyu8-3tfj-gge3-4txf-29486123s1gn 8dv3p0gv-3xfj-gjz3-4tgk-3g483123f68l 9i78i7bh-3ofj-lge3-4tlf-2o486g23hbzw 8xh7fgvz-19fj-gye3-4zgf-294831b311gb 9dw996rt-33fj-zge3-4ugf-z9486123359g
file2中每行以the_id:开头的行,其前一行是对应的名称(名称无固定格式),内容如下:
name1 the_id: 8dh4rfvj-39fj-gje3-4tgf-2948l12356vz name3 the_id: 9i78i7bh-3ofj-lge3-4tlf-2o486g23hbzw name9 name8 the_id: 9dh5hyu8-3tfj-gge3-4txf-29486123s1gn name7 the_id: 8xh7fgvz-19fj-gye3-4zgf-294831b311gb name5 the_id: 9dw996rt-33fj-zge3-4ugf-z9486123359g name10 the_id: 8dv3p0gv-3xfj-gjz3-4tgk-3g483123f68l name12 name20
需求
遍历两个文件,若file2中的ID存在于file1中,则删除该the_id:行及其前一行;否则保留名称行。预期输出:
name9 name12 name20
此前尝试的代码
首次尝试
grep -oE "[0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}" file1 | while read -r line do while read -r line2 do if [[ "$line" =~ ^"$line2"$ ]] then echo "$line == $line2" else echo "$line != $line2" fi done < file2 done
更新后的尝试
while read -r line do while read -r line2 do if [[ "$line" =~ ^"$line2"$ ]] then echo "$line == $line2" else echo "$line != $line2" fi done < <(grep -oE "[0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}" file1) done < file2
解决方案
可以用awk高效实现需求,逻辑是先将file1的ID存入数组,再遍历file2时跟踪前一行内容,判断是否需要跳过对应行:
脚本文件方式
创建script.awk文件:
# 读取file1的所有ID存入数组ids NR == FNR { ids[$0] = 1 next } # 处理file2 { # 当前行是the_id开头的情况 if ($0 ~ /^the_id: /) { id = substr($0, 8) # 若ID在file1中,标记前一行需跳过 if (id in ids) { skip_prev = 1 } else { # 否则输出前一行名称 print prev_line skip_prev = 0 } } else { # 当前行是名称行的情况 if (!skip_prev && prev_line != "") { print prev_line } skip_prev = 0 prev_line = $0 } } # 处理最后一行未输出的名称 END { if (!skip_prev && prev_line != "") { print prev_line } }
执行命令:
awk -f script.awk file1 file2
单行命令方式
awk 'NR==FNR{ids[$0]=1;next} {if($0~/^the_id: /){id=substr($0,8);if(id in ids){skip_prev=1}else{print prev_line;skip_prev=0}}else{if(!skip_prev && prev_line!="")print prev_line;skip_prev=0;prev_line=$0}} END{if(!skip_prev && prev_line!="")print prev_line}' file1 file2
逻辑解释
- 读取file1:利用
NR==FNR判断当前处理第一个文件,将每行ID存入数组ids。 - 处理file2:
- 遇到
the_id:行时,提取ID并检查是否在ids数组中,存在则标记跳过前一行,否则输出前一行名称。 - 遇到名称行时,若无需跳过前一行则输出之前保存的名称,再将当前行设为下一行的前一行。
- 遇到
- 收尾处理:在END块输出最后一行未处理的名称(如果无需跳过)。
内容的提问来源于stack exchange,提问作者Saeed
相关产品推荐
相关产品推荐

