我有一个如下文件。根据分数列,我想通过打印整行如下来输出每个科目得分最高的人。不需要特定的顺序,但主题列应该包含所有唯一的条目。
name subject score remarks
john Math 67 satis
lewis History 56 poor
sarah Math 89 good
fiona Geo 65 satis
george History 99 very good
所需输出:
name subject score remarks
sarah Math 89 good
george History 99 very good
fiona Geo 65 satis
所有列均以制表符分隔,只有备注列具有空格分隔的单词。如果同一科目存在相同的分数,我想输出所有这些人。
答案1
就像是:
{
head -n 1 file.txt &&
tail -n+2 file.txt |
sort -t $'\t' -nrk 3 |
awk -F '\t' 's[$2] && s[$2] > $3 { next }{ s[$2] = $3 } 1'
} >outfile.txt
head -n1
打印标题。tail -n+2
跳过标头和管道其余部分进行处理。sort
进行数字反向排序(从高到低)(-rn
)。按输入的第 3 列 (-k 3
) 进行排序,并用制表符分隔(-t $'\t'
如果名称或主题列中没有空格,则可以删除 - )。awk
只要没有看到主题,就打印行,或者等于最后一行。
-F '\t'
将字段分隔符设置为制表符。如果名称和主题列不包含空格,则可以删除。s[$2] && s[$2] > $3 { next }
下一个; ifs[subject]
已设置且大于当前值(最高分)。{s[$2] = 1}
放s[subject] = score
1
打印
如果您不介意最后一行的标题并且不需要考虑名字或者科目带空格(如:没有)——可以缩短为:
sort -nrk3 file.txt | awk ' s[$2] && s[$2] > $3{next}{s[$2]=$3}1'
答案2
awk 'BEGIN{ FS=OFS="\t" }
NR==1 { print; next }
max[$2]<$3 || NR==2{ max[$2]=$3; data[$2]=$0; next }
max[$2]==$3 { data[$2]= data[$2] ORS $0 }
END{ for(x in data) print data[x] }' infile
BEGIN{ FS=OFS="\t" }
,设置制表\t
符为F产量S输入的分隔符和氧输出F产量S输出的分隔符。NR==1{ print; next }
,输出标题行。max[$2]<$3 || NR==2{ max[$2]=$3; data[$2]=$0; next }
,更新并保留每个科目的最高分,并保留该高分科目的整行。max[$2]==$3{ data[$2]= data[$2] ORS $0 }
,附加每个相同科目具有相同最高分数的记录。END{ for(x in data) print data[x] }
,循环数据数组并打印最终结果。
答案3
使用 GNUsort
进行排序并uniq
只取每个主题的第一个:
sort -t '\t' -r -k 2 -k 3n scores.txt | uniq -f 1 -w 7
-k 2
按主题(第二个字段)排序,-k 3n
n
并按分数(第三个字段)进行数字排序。该-r
选项会颠倒排序顺序,因此得分最高的将排在第一位。
现在uniq
删除两个主题,跳过第一个字段并-f 1
仅比较 5,因此对于共享前 5 个字符(如HistoryA
和 )的主题,这将失败HistoryB
。
至于使用uniq
带有选项的命令-f
,因此第一个字段不得包含空格,否则会产生错误的输出,因为该字段是一系列空格(通常是空格和/或制表符),然后是非空白字符。某些特定版本仅在 Debian 中uniq
可以-t
选择指定字段分隔符,但现在出于兼容性原因删除了。
答案4
awk 'NR==FNR {if ($3>max[$2]) max[$2]=$3; next} FNR==1||$3==max[$2]' file file
对输入文件进行两次遍历。第一遍记录最高分,第二遍打印行。假设分数大于零。