You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

逻辑解释

  1. 读取file1:利用NR==FNR判断当前处理第一个文件,将每行ID存入数组ids。
  2. 处理file2:
    • 遇到the_id:行时,提取ID并检查是否在ids数组中,存在则标记跳过前一行,否则输出前一行名称。
    • 遇到名称行时,若无需跳过前一行则输出之前保存的名称,再将当前行设为下一行的前一行。
  3. 收尾处理:在END块输出最后一行未处理的名称(如果无需跳过)。

内容的提问来源于stack exchange,提问作者Saeed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 04:01:25