【问题标题】:Extracting information from a line with specific pattern using awk/sed使用 awk/sed 从具有特定模式的行中提取信息
【发布时间】:2026-01-24 22:35:02
【问题描述】:

我有一个这样的文件,即

A   10  20  bob.1   ID=bob.1;Parent=bob;conf=XF;Note=bob_v1
A   20  30  bob.2   ID=bob.2;Parent=bob;Note=bob_v1;conf=XF

使用下面的命令行,我将信息提取为 conf 的单独列。

sed -Ei 's/(.*conf=)([^;]*)(;.*)/\1\2\3\t\2/g' my_file

但是,如果在 conf 的末尾有这个符号 ;有用。否则没有。如何修改脚本以便在这两种情况下提取模式,以及在放置标签为空的情况下?

A   10  20  bob.1   ID=bob.1;Parent=bob;conf=XF;Note=bob_v1  XF
A   20  30  bob.2   ID=bob.2;Parent=bob;Note=bob_v1;conf=XF  XF

我用这个链接作为参考:https://unix.stackexchange.com/questions/414082/extract-part-of-lines-with-specific-pattern-and-store-in-a-new-field-using-awk-o?noredirect=1&lq=1

【问题讨论】:

  • 当你说in case it is empty to put tab - 你的意思是在你上面的输出中有一个标签而不是XF 还是你的意思是上面的XFs应该有一个标签,在空的情况下它'd just be tab then null,还是你的意思是别的?在您的示例输入/输出中包含该案例。

标签: awk sed


【解决方案1】:

您实际上可以删除;

sed -iE 's/(.*conf=)([^;]*)(.*)/\1\2\3\t\2/g'  my_file

[^;]* 是一个否定括号表达式,它只会匹配除 ; 之外的 0 个或多个(由于 *)字符,因此 ; 不必出现在模式本身中,前面的模式已经被“限制”了。

online sed demo

s="A   10  20  bob.1   ID=bob.1;Parent=bob;conf=XF;Note=bob_v1
A   20  30  bob.2   ID=bob.2;Parent=bob;Note=bob_v1;conf=XF"
sed -E 's/(.*conf=)([^;]*)(.*)/\1\2\3\t\2/g' <<< "$s"

输出:

A   10  20  bob.1   ID=bob.1;Parent=bob;conf=XF;Note=bob_v1 XF
A   20  30  bob.2   ID=bob.2;Parent=bob;Note=bob_v1;conf=XF XF

【讨论】:

    【解决方案2】:

    请尝试关注awk

    awk 'match($0,/conf=[^;]*/){print $0,substr($0,RSTART+5,RLENGTH-5);next} 1' Input_file
    

    说明:现在为上述代码添加说明。

    awk '                                        ##Starting awk program here.
    match($0,/conf=[^;]*/){                      ##Using match function of awk to match regex from string conf= till semi colon comes.
       print $0,substr($0,RSTART+5,RLENGTH-5)    ##Printing current line and then sub-string whose starting point of RSTART+5 and ending point is RLENGTH-5
       next                                      ##next will skip all further statements from here.
    }                                            ##Closing BLOCK for match function here.
    1                                            ##Mentioning 1 will print lines, those ones which are not having conf string match so it will simply print them.
    '  Input_file                                ##Mentioning Input_file name here.
    

    输出如下。

    A   10  20  bob.1   ID=bob.1;Parent=bob;conf=XF;Note=bob_v1 XF
    A   20  30  bob.2   ID=bob.2;Parent=bob;Note=bob_v1;conf=XF XF
    

    【讨论】:

      【解决方案3】:

      每当您有 name=value 输入数据时,我发现创建一个表示该关系的数组(下面的f[name]=value)最简单、最健壮、最灵活等,这样您就可以通过它们的名称访问这些值。取决于in case it is empty to put tab 的含义:

      $ awk -F'[[:space:];=]+' -v OFS='\t' '
          {delete f; for (i=5; i<NF; i+=2) f[$i]=$(i+1); print $0, f["conf"]}
      ' file
      A   10  20  bob.1   ID=bob.1;Parent=bob;conf=XF;Note=bob_v1     XF
      A   20  30  bob.2   ID=bob.2;Parent=bob;Note=bob_v1;conf=XF     XF
      

      或:

      $ awk -F'[[:space:];=]+' '
          {delete f; f["conf"]="\t"; for (i=5; i<NF; i+=2) f[$i]=$(i+1); print $0, f["conf"]}
      ' file
      A   10  20  bob.1   ID=bob.1;Parent=bob;conf=XF;Note=bob_v1 XF
      A   20  30  bob.2   ID=bob.2;Parent=bob;Note=bob_v1;conf=XF XF
      

      【讨论】:

        【解决方案4】:

        你可以试试 Perl 单行代码

        $ perl -lne ' /conf=(\w+)/ and $_.=" $1"; print ' conf.txt
        A   10  20  bob.1   ID=bob.1;Parent=bob;conf=XF;Note=bob_v1 XF
        A   20  30  bob.2   ID=bob.2;Parent=bob;Note=bob_v1;conf=XF XF
        $
        

        甚至更短

        $ perl -lne ' /conf=(\w+)/ and print "$_ $1" ' conf.txt
        A   10  20  bob.1   ID=bob.1;Parent=bob;conf=XF;Note=bob_v1 XF
        A   20  30  bob.2   ID=bob.2;Parent=bob;Note=bob_v1;conf=XF XF
        

        【讨论】:

          【解决方案5】:

          我们不应该要求\3 中的;——因为它已经在\2 的排除字符列表中处理过:

          sed -Ei 's/(.*conf=)([^;]*)(.*)/\1\2\3\t\2/' my_file
          

          如果我们需要使用除; 之外的其他字符作为分隔符,我们将其包含在\2 的字符列表中。这样的字符可以是\t 还是空格?

          sed -Ei 's/(.*conf=)([^;\t ]*)(.*)/\1\2\3\t\2/' my_file
          

          【讨论】:

            【解决方案6】:

            of my answer 或多或少直接复制到此问题中链接到的问题:

            BEGIN { OFS = FS = "\t" }
            
            function get_attrib_by_name(key,  n,attrib,kv) {
                # Split the attribute field on semi-colons.
                n = split($5, attrib, ";")
            
                # Loop over the attributes and split each on "=".
                # When we've found the one we're looking for (by key name in "key"),
                # return the corresponding value.
                for (i = 1; i <= n; ++i) {
                    split(attrib[i], kv, "=")
                    if (kv[1] == key) {
                        return kv[2]
                    }
                }
            }
            
            # Using the above function.
            {
                name = get_attrib_by_name("conf")
                print $0, name
            }
            

            测试:

            $ awk -f script.awk file.gff
            A       10      20      bob.1   ID=bob.1;Parent=bob;conf=XF;Note=bob_v1 XF
            A       20      30      bob.2   ID=bob.2;Parent=bob;Note=bob_v1;conf=XF XF
            

            【讨论】: