【问题标题】:Awk printing out smallest and highest number, in a time formatawk 以时间格式打印出最小和最大的数字
【发布时间】:2017-10-11 16:17:16
【问题描述】:

我是 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   

【问题讨论】:

  • 你试过这样的事情吗?他们将最后一列分成 3 列(小时、分钟、秒)并使用它们进行排序 stackoverflow.com/questions/22053402/…
  • 只是补充一下,我意识到我尝试的输出是按顺序对它们进行排序,这只是我测试这是否可以确定最高和最低 - 而不是它是否会打印出最高最低,这只是一个起点。最终目的是打印出最高时序和最低时序。
  • 我会试试您发送的链接中描述的内容并更新结果。
  • 您的输入文件中真的有标题行吗? edit 你的问题是提供几行输入(我们不能有用地对 1 行进行排序!)加上所需的输出。确保在示例中包含您认为脚本难以处理的所有边缘情况。
  • 您的第 3 列是否包含持续时间或时间?

标签: bash awk


【解决方案1】:

之后问题澄清:如果时间字段在同一个地方总是有相同的位数,例如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 &lt; b(例如"1:22:33" &lt; "1:23:00")我们知道ab“小”。 (如果值的格式不一致,则仅使用字典比较,我们无法对它们进行排序,例如"12:00:00" &lt; "3:00:00"。)

因此,在读取第一个值(第一条记录,NR==1)时,我们将初始最小值/最大值设置为读取时间(在第三个字段中)。对于每条记录,我们测试当前值是否小于当前最小值,如果是,我们设置新的最小值。同样对于最大值。我们使用短路而不是if 来缩短表达式($3&lt;m &amp;&amp; m=$3 等效于if ($3&lt;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
}

对于每一行,我们将第三个字段中的时间分量(小时、分钟、秒)转换为秒,稍后我们可以将其简单地作为数字进行比较。当我们迭代时,我们跟踪当前的 min val 和 max val,将它们打印在 END 中。 min 和 max 的初始值取自第一行 (NR==1)。

【讨论】:

  • 您不必做任何时间数学,只需将整个时间视为一个数字
  • 谢谢,这回答了我的问题,但更重要的是,它帮助我了解每一步都发生了什么。我对这种东西完全陌生,所以它有助于解释和解决方案。
  • @123,不知道。谢谢,我会更新我的答案。
  • @EdMorton,你是对的,这是更好的方法,会解决的。谢谢!
  • @123,事实证明你不能那样做。如果您将整个时间字段转换为数字,您将只得到前导数字(小时)。如果将值作为字符串进行比较,21:.. 将出现在 3:.. 之前。字符串比较仅在特殊情况下有效,所有值都符合单一格式(1 位数小时)。
【解决方案2】:

鉴于您的陈述,时间字段实际上是一个持续时间,而小时部分始终是一位数,这就是您所需要的:

$ 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

【讨论】:

    【解决方案3】:

    您不想在 awk 中运行 sort(即使使用正确的语法)。

    试试这个:

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

    在哪里

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

    【讨论】:

    • 您好,感谢您的回复!我已经使用了它并理解了每一步,但是我只得到一个输出 - 但是我得到的输出确实是列表中最大的时间,但最小的时间似乎没有显示?一个可能的原因虽然我对这些东西并不完全先进,但最大的时间是唯一在冒号前有 2 的时间,所有其他时间都以 1 开头 - 可能是它正在打印最大的但不是其他,因为它仅按冒号前的第一个数字排序。没有意义,因为它仍然应该打印第一行。
    猜你喜欢
    • 2020-07-02
    • 2019-10-17
    • 1970-01-01
    • 1970-01-01
    • 2018-09-05
    • 1970-01-01
    • 2018-06-08
    • 2018-06-02
    • 1970-01-01
    相关资源
    最近更新 更多