Awk打印出最小和最大数字,以时间格式呈现

3

我是一个对Linux/bash shell比较新手的人,我在尝试从文本文件中打印出一列中最大值和最小值时遇到了麻烦。该文件的格式如下:

Geoff        Audi           2:22:35.227
Bob          Mercedes       1:24:22.338
Derek        Jaguar         1:19:77.693
Dave         Ferrari        1:08:22.921

如您所见,最后一列是一个时间戳,我正在尝试使用awk打印出该列中的最高和最低时间戳。我很困惑,已经尝试过以下命令:


awk '{print sort -n <  $NF}' timings.txt 

然而,那似乎并没有解决问题,我只收到了一个输出:

1
0
1
0
...

重复了很多次,时间也更长了,但是我不想让它在第一次或两次迭代之后就变得非常冗长。

我的期望输出是:

Min: 1:08:22.921
Max: 2:22:35.227   

只是补充一下,我意识到我尝试的输出是按顺序排序,那只是我测试是否可以确定最高和最低值 - 而不是它是否会打印出最高和最低值,这只是一个起点。最终目标是打印出最高时间和最低时间。 - user5791847
我会尝试一下你发来链接中描述的内容,并更新结果。 - user5791847
你的输入文件中真的有标题行吗?[编辑]你的问题,提供几行输入(我们无法有用地排序1行!)以及所需的输出。确保在示例中包含您认为脚本难以处理的所有边缘情况。 - Ed Morton
为什么你会期望那样能够运行? - 123
因为我基本上对bash和awk完全陌生。 - user5791847
显示剩余5条评论
3个回答

2
在澄清问题之后:如果时间字段始终在相同位置具有相同数量的数字,例如 h:mm:ss.ss,则解决方案可以大大简化。换句话说,我们不再需要将时间转换为秒来进行比较,而是可以进行简单的字符串/字典比较。
$ awk 'NR==1 {m=M=$3} {$3<m&&m=$3; $3>M&&M=$3} END {printf("min: %s\nmax: %s",m,M)}' file
min: 1:08:22.921
max: 2:22:35.227

逻辑与下面的脚本相同,只是使用基于字符串比较的更简单的方式来排序值(确定最小/最大值)。我们可以这样做,因为我们知道所有时间都符合相同的格式,如果a < b(例如"1:22:33" < "1:23:00"),我们就知道ab“小”。 (如果值的格式不一致,则仅使用字典比较无法对其进行排序,例如"12:00:00" < "3:00:00"。)
所以,在第一次读取值时(第一条记录,NR==1),我们将初始的最小/最大值设置为读取的时间(在第三个字段中)。对于每条记录,我们测试当前值是否小于当前最小值,如果是,则设置新的最小值。类似地,对于最大值也是如此。我们使用短路运算而不是if来使表达式更短($3<m && m=$3等价于if ($3<m) m=$3)。在END中,我们只需打印结果。
这是一个通用的awk解决方案,它接受每个记录的小时/分钟/秒数的变量位数的时间字符串。
$ awk '{split($3,t,":"); s=t[3]+60*(t[2]+60*t[1]); if (s<min||NR==1) {min=s;min_t=$3}; if (s>max||NR==1) {max=s;max_t=$3}} END{print "min:",min_t; print "max:",max_t}' file
min: 1:22:35.227
max: 10:22:35.228

或者,更易读的形式如下:
#!/usr/bin/awk -f
{
    split($3, t, ":")
    s = t[3] + 60 * (t[2] + 60 * t[1])
    if (s < min || NR == 1) {
        min = s
        min_t = $3
    }
    if (s > max || NR == 1) {
        max = s
        max_t = $3
    }
}

END {
    print "min:", min_t
    print "max:", max_t
}

对于每一行,我们将第三个字段中的时间组件(小时,分钟,秒)转换为秒,以便稍后可以将它们作为数字进行简单比较。在迭代过程中,我们跟踪当前的最小值和最大值,并在 END 中打印它们。最小和最大值的初始值来自第一行(NR==1)。


1
你不需要进行任何时间计算,只需将整个时间视为一个单独的数字即可。 - 123
谢谢,这回答了我的问题,更重要的是它帮助我理解每一步发生了什么。我完全是新手,所以有解释和解决方案会很有帮助。 - user5791847
@EdMorton,你说得对,这是更好的方法,我会修复的。谢谢! - randomir
@randomir,另外作为参考,如果我想在不同的表上执行此操作,并更改它所操作的列,我只需将所有$3更改为$X,其中X是我希望执行操作的列吗? - user5791847
@SeanC,没错,$n 是第 n 个字段。只需将 $3 更改为 $n 即可。 - randomir
显示剩余4条评论

1
考虑到您提到时间字段实际上是一个持续时间,并且小时组件始终是一位数字,这就是您需要的全部内容:
$ awk 'NR==1{min=max=$3} {min=(min<$3?min:$3); max=(max>$3?max:$3)} END{print "Min:", min ORS "Max:", max}' file
Min: 1:08:22.921
Max: 2:22:35.227

0

你不想在 awk 内部运行 sort(即使使用正确的语法)。

尝试这个:

sed 1d timings.txt | sort -k3,3n | sed -n '1p; $p'

在哪里

  • 第一个sed将删除标题
  • 按第3列进行数字排序
  • 第二个sed将打印第一行和最后一行

嗨,谢谢回复!我已经使用了这个并且理解了每一步,但是我只得到了一个输出 - 我得到的输出确实是列表中最大的时间,但最小的似乎没有显示?可能的原因是,虽然我对这些东西不是很熟练,但最大的时间是唯一一个在冒号前有2的时间,其他所有时间都以1开头 - 它是否只按冒号前的第一个数字排序,因此仅打印最大的而不是其他的。这不合理,因为它仍然应该打印第一行。 - user5791847

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接