使用awk查找每个块的列中的最小值

时间:2017-10-25 05:15:09

标签: bash awk

我想为每个具有"标题行"的块找到$ 3列的最小值。格式为' @ value1 value2'。该文件看起来像这样

@ 62.65 -50.35
0 1.50 1.676
1.67 1.50 1.677
1.67 2.25 1.423
2.90 2.25 2.902
2.90 4.95 2.903
3.04 4.95 3.049
@ 63.61 -50.45
0 1.50 1.654
3.42 1.50 1.875
3.43 2.19 3.430
5.31 2.19 1.032
5.32 6.23 5.320
5.43 6.23 5.434

在此之后,我想打印标题以及最小$ 3的整行。

所以,我的输出文件看起来应该是

@ 62.65 -50.35
1.67 2.25 1.423
@ 63.61 -50.45
5.31 2.19 1.032

这是我到目前为止所尝试的:

awk '{if (!/^@/) {if ($3 < min) {$3=min;} print $1, $2, $3, min; min = $3;} else if (/^@/) min = 10; print $1 $2 $3;}' input.txt > output.txt

我有麻烦将块分开并重置&#34;重置&#34; min(我试图将&#39; start&#39;值设置为高 - 即10)。

我是编程新手,到目前为止主要使用awk - 如果你可以帮助我,那真的很棒!非常感谢!干杯,Isi

4 个答案:

答案 0 :(得分:3)

使用awk:

awk '/^@/{if(h){print h RS m}min=""; h=$0; next}min=="" || $3 < min{min=$3; m=$0}END{print h RS m}' infile

使用array

awk '/^@/{h=$0;min="";next}min==""||$3<min{min=$3;l[h]=$0}END{for(i in l)print i RS l[i]}' infile

更好的可读性:

awk '/^@/{
          if(h){
               print h RS m
          }
          min=""; h=$0; next
         }
     min=="" || $3 < min{
          min=$3; 
          m=$0
     }
     END{
          print h RS m
     }
    ' infile

使用array

awk '/^@/{
          h=$0;min="";
          next
     }
     min==""||$3<min{
          min=$3;
          l[h]=$0
     }
     END{
          for(i in l)
               print i RS l[i]
     }
     ' infile

测试结果:

$ cat infile
@ 62.65 -50.35
0 1.50 1.676
1.67 1.50 1.677
1.67 2.25 1.423
2.90 2.25 2.902
2.90 4.95 2.903
3.04 4.95 3.049
@ 63.61 -50.45
0 1.50 1.654
3.42 1.50 1.875
3.43 2.19 3.430
5.31 2.19 1.032
5.32 6.23 5.320
5.43 6.23 5.434

输出-1(推荐)

$ awk '/^@/{if(h){print h RS m}min=""; h=$0; next}min=="" || $3 < min{min=$3; m=$0}END{print h RS m}' infile
@ 62.65 -50.35
1.67 2.25 1.423
@ 63.61 -50.45
5.31 2.19 1.032

输出-2

$ awk '/^@/{h=$0;min="";next}min==""||$3<min{min=$3;l[h]=$0}END{for(i in l)print i RS l[i]}' infile
@ 62.65 -50.35
1.67 2.25 1.423
@ 63.61 -50.45
5.31 2.19 1.032

答案 1 :(得分:1)

根据以下脚本,Awk可以很容易地做到这一点:

awk '
    $1=="@"    { first=1; key=$0; next }
    first==1   { lowest=$3; line[key]=$0; first=0; next }
               { if ($3 < lowest) { lowest=$3; line[key]=$0 } }
    END        { for (key in line) { printf "%s\n%s\n", key, line[key] } }
' <<EOF
@ 62.65 -50.35
0 1.50 1.676
1.67 1.50 1.677
1.67 2.25 1.423
2.90 2.25 2.902
2.90 4.95 2.903
3.04 4.95 3.049
@ 63.61 -50.45
0 1.50 1.654
3.42 1.50 1.875
3.43 2.19 3.430
5.31 2.19 1.032
5.32 6.23 5.320
5.43 6.23 5.434
EOF

根据要求,输出为:

@ 62.65 -50.35
1.67 2.25 1.423
@ 63.61 -50.45
5.31 2.19 1.032

打破代码澄清:

$1=="@" {                # For all header lines:
    first=1              #    Flag that you're starting a new block.
    key=$0               #    Save the key.
    next                 #    Go back for next line.
}
first==1 {               # For first line in each block:
    lowest=$3            #    It must be lowest.
    line[key]=$0         #    Store line.
    first=0              #    Now processing subsequent lines in block.
    next                 # Go back for next line.
}
{                        # For non-first-lines-in-block:
    if ($3 < lowest) {   #    Only if this one is lower.
        lowest=$3        #    Store value and line.
        line[key]=$0
    }
}
END {                    # At end, simply output associative array.
    for (key in line) {
        printf "%s\n%s\n", key, line[key]
    }
}

请记住,假设标题行是唯一的。如果可能存在重复项并且您希望对其进行明确处理,则可以通过NR$0的组合创建密钥。

答案 2 :(得分:1)

这是一种完全不同的awk方法。

awk 'BEGIN {RS="@"} {s=$3 OFS $4 OFS $5; n=$5; for (i=5;i<=NF;i+=3) {if ($i<n) {s=$(i-2) OFS $(i-1) OFS $i; n=$5} }} n { print "@ " $1 OFS $2 ORS s }' infile

或者分发以便于评论:

BEGIN {
  RS="@"                          # "@" as our record separator
}

{
  s=$3 OFS $4 OFS $5              # store the first line...
  n=$5
  for (i=5;i<=NF;i+=3) {          # for each 3rd field on a line,
    if ($i<n) {                   # test its value and
      s=$(i-2) OFS $(i-1) OFS $i  # store a new value if the
      n=$5                        # condition matches
    }
  }
}

n {
  print "@ " $1 OFS $2 ORS s      # print records once we have them.
}

答案 3 :(得分:0)

以下是使用awk命令的另一种解决方案。

awk '
/^@/{if(b)print a;print $0;next}
!b||$3<b{b=$3;a=$0}
END{print a}
' infile

/^@/{if(b)print a;print $0;next}

表示以@

开头的行

如果定义了b,则打印a(在第一行a和b未定义)

打印该行并转到下一行

!b||$3<b{b=$3;a=$0}

表示每行不包含以@

开头的行

如果未定义b或$ 3小于b,则保留3美元b并保持行

END{print a}

最后,我们必须打印存储在

中的行