找到两个模式(0 和 1)之间 $4 的最大值并将其设置为变量

找到两个模式(0 和 1)之间 $4 的最大值并将其设置为变量

我正在尝试编写一个 .awk 源文件来过滤 .txt,我想知道如何在第二个命令中使用 max 变量

BEGIN {max1=0} 

找到两个模式(0 和 1)之间 $4 的最大值并将其设置为变量

{if ($4>0 && $4<1)
max1=$4
else if ($4==1)
max=max1}
END {print max}

/Nodes/ {f=1} /EndNodes/ {f=0} #Gives lines after Nodes and before EndNodes
{if ($2+0>=0 && $3+0==0 && max==$4)  #Filters the given lines between Nodes and EndNodes
{print $1}}

我的猜测是,我需要在定义 max 变量后从头开始运行程序(因为它使用与第二个命令相同的行。

期望输出应该是:20、31和32

输入

$Nodes
  34
   1  0.0000000E+000  0.0000000E+000  0.0000000E+000
   2  6.0000000E-003  0.0000000E+000  0.0000000E+000
   3  0.0000000E+000  6.0000000E-003  0.0000000E+000
   4 -6.0000000E-003  0.0000000E+000  0.0000000E+000
   5  0.0000000E+000 -6.0000000E-003  0.0000000E+000
   6  2.1213203E-003  2.1213203E-003  0.0000000E+000
   7 -2.1213203E-003  2.1213203E-003  0.0000000E+000
   8 -2.1213203E-003 -2.1213203E-003  0.0000000E+000
   9  2.1213203E-003 -2.1213203E-003  0.0000000E+000
  10  4.2426407E-003  4.2426407E-003  0.0000000E+000
  11 -4.2426407E-003  4.2426407E-003  0.0000000E+000
  12 -4.2426407E-003 -4.2426407E-003  0.0000000E+000
  13  4.2426407E-003 -4.2426407E-003  0.0000000E+000
  14  2.1213203E-003  0.0000000E+000  0.0000000E+000
  15  0.0000000E+000  2.1213203E-003  0.0000000E+000
  16 -2.1213203E-003  0.0000000E+000  0.0000000E+000
  17  0.0000000E+000 -2.1213203E-003  0.0000000E+000
  18  0.0000000E+000  2.1213203E-003  6.0000000E-003
  19  0.0000000E+000  6.0000000E-003  6.0000000E-003
  20  0.0000000E+000  0.0000000E+000  6.0000000E-003
  21 -4.2426407E-003  4.2426407E-003  6.0000000E-003
  22 -2.1213203E-003  2.1213203E-003  6.0000000E-003
  23 -6.0000000E-003  0.0000000E+000  6.0000000E-003
  24 -2.1213203E-003  0.0000000E+000  6.0000000E-003
  25 -4.2426407E-003 -4.2426407E-003  6.0000000E-003
  26 -2.1213203E-003 -2.1213203E-003  6.0000000E-003
  27  0.0000000E+000 -6.0000000E-003  6.0000000E-003
  28  0.0000000E+000 -2.1213203E-003  6.0000000E-003
  29  4.2426407E-003 -4.2426407E-003  6.0000000E-003
  30  2.1213203E-003 -2.1213203E-003  6.0000000E-003
  31  6.0000000E-003  0.0000000E+000  6.0000000E-003
  32  2.1213203E-003  0.0000000E+000  6.0000000E-003
  33  4.2426407E-003  4.2426407E-003  6.0000000E-003
  34  2.1213203E-003  2.1213203E-003  6.0000000E-003
$EndNodes
$Elements
#And some more data
$EndElements

答案1

这是一个一次性解决方案:

/Nodes/         { read = 1 }
/EndNodes/      { read = 0 }

!read           { next     }

NF == 4                         { n = $1; x = $2; y = $3; z = $4 }
z > max                         { delete set; i = 1; max = z     }
x >= 0 && y == 0 && z == max    { set[i++] = n                   }

END             { for (i in set) { print set[i] } }

read变量决定我们是否应该对当前记录执行操作。如果是 1,那么我们就这样做。

如果我们对当前输入不感兴趣,则第三个块会丢弃当前输入,并从顶部继续处理下一条记录。

第 4个块设置了四个便利变量 、nxy。它们比其他的z更容易阅读。$1

第 5 个块删除数组set。该set数组是我们迄今为止找到的并且满足条件的所有节点号的集合。由于如果我们找到 的新最大值z,则会执行此块,因此所有先前找到的节点都会失效。我们还保存新的最大值 ( max)。该变量i只是数组的索引(基本上是一个计数器)。如果尚未找到最大值,则max在测试中未初始化的将被视为零。

当我们找到满足条件的节点时,将执行第 6 个块。节点号保存在set数组中并i递增。

最后,我们循环遍历set数组并输出其内容。

在GNU下运行的结果awk

20
31
32

BSDawkmawk在 OpenBSD 上运行以相反的顺序生成列表。

答案2

awk解决方案:

get_max_nodes.awk脚本:

#!/bin/awk -f
BEGIN{ max=0 }
NR==FNR{                         # processing the 1st input file
    if ($4~/^[0-9]/) {           # if the 4th field is a number
        if($4+0 > max) max=$4+0  # capturing maximal number
    }
    next
}
{   # processing the 2nd input file (same file)
    if ($4~/^[0-9]/ && $2+0>=0 && $3+0==0 && $4+0==max) {
        print $1
    }
}

用法:

awk -f get_max_nodes.awk input.txt input.txt

输出:

20
31
32

答案3

我没有足够的声誉来发表评论,所以我被迫回答。我的第一条评论是 awk 不是进行实数数学的最佳工具。它更擅长字符串和整数。

awk 的其他要点: BEGIN 段落发生在从输入中读取任何行之前。 END 段落发生在所有行都被读取之后。

注意:awk 不会保存或关心除当前输入行/记录之外的任何内容,除非您采取措施在代码中存储记录/字段。这需要在 BEGIN 和 END 之间发生。

这些段落之间的代码仅设置/重置变量,但不对它们的值执行任何操作。本质上,您可以尾部 -1 输入文件并通过管道将输出进行剪切,以获得类似的结果。

看来您的意图是测试第 4 列中的数字以找到 max 或 max1,并且如果第 4 列包含此 max,则仅打印第 1 列,然后成功测试第 2 列和第 3 列。此逻辑需要移到 END 之前除非您只关心文件的最后一行。

请记住,在 awk 中,每一行输入(默认情况下)都会与每个条件进行比较。如果条件为真,则执行该操作或操作列表。同一行上很可能有多个条件触发操作。

我的第一印象是您需要重新评估您的流程。确定重要性顺序并采取相应的行动。例如,对我来说,第一重要顺序仅作用于输入文件中两个标志之间的数据。接下来,确定 4 个字段中每个字段中的实数是否可以转换为(或视为)整数(或字符串)而不丢失其含义。实际数据本身不需要更改,只需更改其在代码中的表示即可。这些可以再次转换回来,但失去原始数字准确性的可能性很高。最后,选择是否需要存储所有/任何未排序的随机数据以供以后处理,或者立即提供每行输出。

下面的元示例可以更有效地完成......以一个标志开始,让您知道是否是时候开始解析。你见过 $0 ~= /Nodes/ 行吗?开始=0。您可能还想通过测试 $0 ~= /EndNodes/ line, stop=0 来准备了解何时停止解析数据。如果您要存储数据,您可能需要一个计数器,count=0。

BEGIN {
   start=0
   stop=0
   count=0
   max=0
}
/EndNodes/ {
   stop=1
}
/Nodes/ {
   start=1
}
NF==4 {
   if (start==1 && stop==0) {
      count++
      column1[count]=$1
      column2[count]=substr($2,1,index($2,".")-1)
      column3[count]=substr($3,1,index($3,".")-1)
      column4[count]=substr($4,1,index($4,".")-1)
   }
}
# Now print column1 if column2 is non-negative and column3=0 and column4=max
# In the first loop through the array/list, find max
END {
   for (loop=1;loop<=count;loop++) {
      if (column4[loop]>max) {
         max=column4[loop]
      }
   }
   for (loop=1;loop<=count;loop++) {
      if (column4[loop]==max && column3[loop]==0 && column2[loop]>=0) {
         print column1[loop]
      }
   }
}

如所写,输出将是

20
31
32

相关内容