我有一个名为 file.txt 的文本文件(带有标题行)。我试图提取等于特定列最大值的行(我不知道最大值是多少):
ID t1 q1 t2 q2 q3
1 f 45 ex 1 45
2 r 47 tr 1 33
3 r 33 ex 2 44
4 f 44 s 0 55
5 e 32 ex 0 54
6 f 34 tr 2 46
我需要找到 $5 列的最大值,然后仅打印第 5 列等于该数字的行:
3 r 33 ex 2 44
6 f 34 tr 2 46
我认为以下代码有效,但我的文件很大并且需要很长的时间,所以我正在寻找更快的解决方案(也许使用排序?):
这就是我现在所拥有的:
首先找到最大值:
max=`awk '{print $5}' file.txt | sort -nr | sed -n 2p`
然后选择第 5 列等于该值的行:
awk 'NR>1' file.txt|while read LINE; do value=`echo $LINE|awk '{print $5}'`; if [ $value -eq $max ]; then echo $LINE >> test.txt; fi; done
答案1
一种方法是读取文件一次以获取max
然后再次打印相关行:
max=$(awk 'NR>1 && $5>max {max=$5}; END{print max}' file.txt) &&
awk -v max="$max" '$5==max' file.txt
或者,更简洁地说:
awk -v m="$(awk '(NR>1 && $5>m){m=$5};END{print m}' file.txt)" '$5==m' file.txt
这里的技巧是 awk 的-v
标志,它允许您将变量传递给awk
.在这种情况下,我首先计算最大值,然后将其awk
作为变量给出max
。
答案2
这是一个相当典型的问题,存在一个惯用的 awk 解决方案,涉及对文件进行两次传递。在第一遍中,确定 的最大值$5
,在第二遍中,提取 中包含该最大值的记录$5
。这是一个简单的例子。
awk 'NR == FNR && NR > 1{max = max < $5? $5: max; next}; $5 == max{print}' file.txt file.txt
答案3
如果内存使用不是真正的问题,perl 中的一次性版本可能是:
perl -ane 'END { $"=""; print "@res"; } if($F[4] =~ /^\d+/ and $F[4] > $max) {
$max = $F[4]; @res = (); } push @res, $_ if($F[4] =~ /^\d+$/ and $max == $F[4]);' infile
-n
infile
告诉 perl一次处理一行,将每一行传递给 中指定的命令-e
。告诉-a
perl 展开字段分隔符周围的每一行(默认为空格),并将其分配给一个名为 的数组@F
。结果是我们可以处理每一行并使用它$F[n]
来引用该行的第 n 个元素。
Perl 本身:
END { $"=""; print "@res"; } # at the end of execution set the field separator to
# empty and print the contents of @res, which includes
# newlines when the matching rows were stored
if($F[4] =~ /^\d+/ and $F[4] > $max) { # if the 5th element of the line is solely a
$max = $F[4]; @res = (); # number and it's greater than $max (which
} # starts as undefined), set $max to this number
# and empty the @res results array.
push @res, $_ # push this line to @res ...
if($F[4] =~ /^\d+$/ and $max == $F[4]); # IF the 5th element is solely a
# number and equal to $max
逻辑是$max
从未定义开始且@res
数组为空。当我们第一次在第 5 列中找到一个数字时,我们将其存储在$max
并为空@res
。@res
如果我们在后续行的第 5 列中找到新的最大值,这也会产生清空的副作用。作为单独的检查,如果第 5 列等于,则$max
将此行添加到@res
(存储具有当前最大值的行)。对所有行重复此操作,然后END { }
执行该块,该块打印结果数组的内容,@res
而无需通常在 中设置的前导空格字段分隔符$"
。
这也可能在 中实现awk
,但我的awk-fu
并不是那么好!
答案4
按降序对文件中的值进行排序,然后从顶部打印行,直到值发生变化。
sort -k 5n | awk 'NR==1 {max=$5} $5!=max {quit} {print}'