使用具有特定值的字段对文件进行排序
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 }'
也就是说算法如下:
- 按字段和键值分隔符(
|
和 =
)拆分记录
- 遍历元素并搜索
id
键
- 打印下一个元素(
id
键的值)、分隔符和整行
- 按数字排序
- 删除前置标识符以保留记录的结构
处理示例给出输出:
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
或尝试 sed
和 sort
:sed 's/^\(.*id=\([0-9][0-9]*\).*\)$/ /' file | sort -n | sed 's/^[^ ][^ ]* //'
最近需要根据记录的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 }'
也就是说算法如下:
- 按字段和键值分隔符(
|
和=
)拆分记录 - 遍历元素并搜索
id
键 - 打印下一个元素(
id
键的值)、分隔符和整行 - 按数字排序
- 删除前置标识符以保留记录的结构
处理示例给出输出:
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
或尝试 sed
和 sort
:sed 's/^\(.*id=\([0-9][0-9]*\).*\)$/ /' file | sort -n | sed 's/^[^ ][^ ]* //'