【问题标题】:Grep - regular expression to match multiple key-value pairs and return single lineGrep - 正则表达式匹配多个键值对并返回单行
【发布时间】:2026-01-03 04:25:02
【问题描述】:

我有以下输入:

TAG1 VALUE1;TAG2 VALUE2;sometext;TAG3 VALUE3;sometext;TAG4 VALUE4;TAG5 VALUE5;sometext
TAG1 VALUE1;sometext;TAG3 VALUE3;sometext;TAG4 VALUE4;TAG5 VALUE5;sometext
TAG1 VALUE1;TAG2 VALUE2;sometext;TAG3 VALUE3;sometext;TAG4 VALUE4;TAG5 VALUE5
TAG1 VALUE1;TAG2 VALUE2;sometext;TAG3 VALUE3;sometext;TAG5 VALUE5;sometext
TAG1 VALUE1;TAG2 VALUE2;sometext;

我需要以下输出:

TAG2 VALUE2;TAG3 VALUE3;TAG5 VALUE5;
TAG3 VALUE3;TAG5 VALUE5;
TAG2 VALUE2;TAG3 VALUE3;TAG5 VALUE5;
TAG2 VALUE2;TAG3 VALUE3;TAG5 VALUE5;
TAG2 VALUE2;

或者,或者与上面相同,但只有值

我尝试了以下方法:

grep -oP '(?<=TAG2 ).*?(?=;)|(?<=TAG3 ).*?(?=;)|(?<=TAG5 ).*?(?=;)'

它有效,但每个值都在单独的行中:

VALUE2
VALUE3
VALUE5

我不能假设所有的行都会有所有的标签,所以我真的需要每行输入就需要一行输出。首选原生 Linux 工具 - sed、awk、grep 等。

谢谢!

【问题讨论】:

    标签: regex grep pcre


    【解决方案1】:

    awk:

    $ awk 'BEGIN{FS=OFS=";"}{print $2, $4, $7}' file
    TAG2 VALUE2;TAG3 VALUE3;TAG5 VALUE5
    
    • BEGIN{FS=OFS=";"} 将输入和输出字段分隔符设置为;
    • {print $2, $4, $7} 基于该分隔符打印第 2、第 4 和第 7 个字段。

    我希望解析键/值对的原因是 必须所有键都存在,并且不一定在同一个 列(但如果存在,至少它们是按特定顺序排列的,TAG1 是 首先,TAG2 下一个等...)

    那么应该是这样的:

    awk -v values="TAG1 TAG5"
           'BEGIN{split(values, vals, " ")}
            {a[$1]=$2}
            END{for (i in vals) print vals[i], a[vals[i]]}'
         RS=";" file
    
    • -v values="TAG1 TAG5" passwes awk 要分析的值。
    • 'BEGIN{split(values, vals, " ")} 将它们存储到 vals[] 数组中。
    • {a[$1]=$2} 保存 a["TAG1"] = "VAL1" 匹配。
    • END{for (i in vals) print vals[i], a[vals[i]]}' 打印匹配项。
    • RS=";" 将记录分隔符设置为 ;,以便我们可以处理文件中的几个值。

    例子:

    $ awk -v values="TAG1 TAG3" 'BEGIN{split(values, vals, " ")} {a[$1]=$2} END{for (i in vals) print vals[i], a[vals[i]]}' RS=";" file
    TAG1 VALUE1
    TAG3 VALUE3
    $ awk -v values="TAG1 TAG6" 'BEGIN{split(values, vals, " ")} {a[$1]=$2} END{for (i in vals) print vals[i], a[vals[i]]}' RS=";" file
    TAG1 VALUE1
    TAG6 
    

    【讨论】:

    • 我要解析键/值对的原因是不一定所有键都存在,也不一定在同一列中(但至少它们按特定顺序排列,如果存在,TAG1 是第一个, TAG2 下一个等...)
    • @lubas 以更广泛的方法查看我的更新答案。
    • 在 27k 行长的输入中,它仅响应 2 行(第一行中的 TAG1 VALUE1 和第二行中的 TAG2 VALUE2)。
    【解决方案2】:

    这是一种perl方式:

    $ perl -ne 'print $1," " while(/(?<=TAG[235] )(.*?)(?=;)/g); print "\n";'  in.txt
    

    根据评论编辑:

    $ perl -ne 'print $1," " while(/(?<=(?:DEV|SRC|DST|ACT) )(.*?)(?=;)/g); print "\n";'  in.txt
    

    实际操作:

    $ cat in.txt
    TAG1 VALUE1;TAG2 VALUE2;sometext;TAG3 VALUE3;sometext;TAG4 VALUE4;TAG5 VALUE5;sometext
    TAG1 VALUE1;sometext;TAG3 VALUE3;sometext;TAG4 VALUE4;TAG5 VALUE5;sometext
    TAG1 VALUE1;TAG2 VALUE2;sometext;TAG3 VALUE3;sometext;TAG4 VALUE4;TAG5 VALUE5
    TAG1 VALUE1;TAG2 VALUE2;sometext;TAG3 VALUE3;sometext;TAG5 VALUE5;sometext
    TAG1 VALUE1;TAG2 VALUE2;sometext;
    
    $ perl -ne 'print $1," " while(/(?<=TAG[235] )(.*?)(?=;)/g); print "\n";'  in.txt
    VALUE2 VALUE3 VALUE5 
    VALUE3 VALUE5 
    VALUE2 VALUE3 
    VALUE2 VALUE3 VALUE5 
    VALUE2 
    

    【讨论】:

    • 这很好,但在现实世界中的标签不是那么相似(例如 DEV、SRC、DST、ACT) - 您的模式可以更新以在这种情况下工作吗?
    • @anubhava:谢谢。
    • @M42 - 效果很好。试图将其转换为 grep/PCRE 但我做不到。 TAG1 也将匹配 TAG11(对于 ma 数据,它是 SRC 和 SRCP 重叠)。感谢您的帮助!
    • @lubas: TAG1TAG11 不匹配,因为 TAGn 后面有空格。
    【解决方案3】:

    使用纯 BASH:

    l='TAG1 VALUE1;TAG2 VALUE2;sometext;TAG3 VALUE3;sometext;TAG4 VALUE4;TAG5 VALUE5;sometext'
    ( IFS=';' && read -a arr <<< "$l"; echo "${arr[1]}; ${arr[3]}; ${arr[6]};" )
    TAG2 VALUE2; TAG3 VALUE3; TAG5 VALUE5;
    
    • IFS=';' 在将行读入数组时将分隔符设为;

    【讨论】:

    • 与 awk 相同 - 不能保证所有标签的存在和特定位置
    • 在这种情况下,我需要一些说明和更多输入/输出示例。
    最近更新 更多