使用具有特定值的字段对文件进行排序

Sorting a file using fields with specific value

最近需要根据记录的ID对几个文件进行排序;问题是可以有几种类型的记录,并且在每一种记录中,我必须用于排序的字段位于不同的位置。但是,由于 key=value 结构,这些字段很容易识别。显示一般结构的简单示例:

fieldA=valueA|fieldB=valueB|recordType=A|id=2|fieldC=valueC
fieldD=valueD|recordType=B|id=1|fieldE=valueE
fieldF=valueF|fieldG=valueG|fieldH=valueH|recordType=C|id=3

我想出了一个如下的管道,它完成了工作:

awk -F'[|=]' '{for(i=1; i<=NF; i++) {if($i ~ "id") {i++; print $i"?"[=11=]} }}' tester.txt | sort -n | awk -F'?' '{print }'

也就是说算法如下:

  1. 按字段和键值分隔符(|=)拆分记录
  2. 遍历元素并搜索 id
  3. 打印下一个元素(id 键的值)、分隔符和整行
  4. 按数字排序
  5. 删除前置标识符以保留记录的结构

处理示例给出输出:

fieldD=valueD|recordType=B|id=1|fieldE=valueE
fieldA=valueA|fieldB=valueB|recordType=A|id=2|fieldC=valueC
fieldF=valueF|fieldG=valueG|fieldH=valueH|recordType=C|id=3

但是,有没有办法使用单个 awk 命令来完成此任务?

您可以尝试使用此 gnu-awk 代码在单个命令中完成此操作:

awk -F'|' '{
   for(i=1; i<=NF; ++i)
      if ($i ~ /^id=/) {
         a[gensub(/^id=/, "", 1, $i)] = [=10=]
         break
      }
}
END {
   PROCINFO["sorted_in"] = "@ind_num_asc"
   for (i in a)
      print a[i]
}' file

fieldD=valueD|recordType=B|id=1|fieldE=valueE
fieldA=valueA|fieldB=valueB|recordType=A|id=2|fieldC=valueC
fieldF=valueF|fieldG=valueG|fieldH=valueH|recordType=C|id=3

我们使用 | 作为字段分隔符,当有一个以 id= 开头的列名时,我们将其存储在数组 a 中,索引作为 = 之后的文本和值作为完整记录。

使用 PROCINFO["sorted_in"] = "@ind_num_asc" 我们使用索引的数值对数组 a 进行排序,然后在 for 循环中我们打印值部分以获得排序后的输出。

仅使用您展示的示例,请尝试以下(awk + sort + cut)解决方案,在 GNU awk 中编写和测试,应该适用于任何awk.

awk '
match([=10=],/id=[0-9]+/){
  print substr([=10=],RSTART,RLENGTH)";"[=10=]
}
' Input_file | sort -t'=' -k2n | cut -d';' -f2-

说明:为以上代码添加详细说明。

awk '                                   ##Starting awk program from here.
match([=11=],/id=[0-9]+/){                  ##Using awk match function to match id= followed by digits.
  print substr([=11=],RSTART,RLENGTH)";"[=11=]  ##printing sub string of matched value followed by current line along with semi-colon in it.
}
' Input_file    |                       ##Mentioning Input_file here and passing awk output as a standard input to next command.
sort -t'=' -k2n |                       ##Sorting output with delimiter of = and by 2nd field then passing output to next command as an input.
cut -d';' -f2-                          ##Using cut command making delimiter as ; and printing everything from 2nd field onwards.     

将 GNU awk 用于 match()sorted_in 的第三个参数:

$ cat tst.awk
match([=10=],/(^|\|)id=([0-9]+)/,a) {
    ids2vals[a[2]] = [=10=]
}
END {
    PROCINFO["sorted_in"] = "@ind_num_asc"
    for ( id in ids2vals ) {
        print ids2vals[id]
    }
}

$ awk -f tst.awk file
fieldD=valueD|recordType=B|id=1|fieldE=valueE
fieldA=valueA|fieldB=valueB|recordType=A|id=2|fieldC=valueC
fieldF=valueF|fieldG=valueG|fieldH=valueH|recordType=C|id=3

试试 Perl:perl -e 'print map { s/^.*? //; $_ } sort { $a <=> $b } map { ($id) = /id=(\d+)/; "$id $_" } <>' file

我使用的代码的一些解释:

print #print the resulting list of lines
    map {
        s/^.*? //;
        $_
    } #remove numeric id from start of line
    sort { $a <=> $b } #sort numerically
    map {
        ($id) = /id=(\d+)/;
        "$id $_"
    } # capture id and place it in start of line
    <> # read all lines from file

或尝试 sedsortsed 's/^\(.*id=\([0-9][0-9]*\).*\)$/ /' file | sort -n | sed 's/^[^ ][^ ]* //'