awk 循环将记录范围写入单独的文件

awk 循环将记录范围写入单独的文件

我有一个包含数千行的文件,描述弹性波模式代码的输出。文件的大小根据频率、模型的厚度以及为特定模型找到的模式数量而变化。标头信息包含找到的模式数量。模式从 0 到 N 编号,并存储在关键字 MODE 旁边。以下是文件如何查找前两种模式。在此示例中,共有 4 种模式,0 到 4。在记录“I DEPTH Y1 Y2 Y3 Y4”之后是一千多个给出幅度的记录。我只显示了前两种模式的前两条记录。使用 awk 和模式 /MODE / 可以很容易地选取各个 MODE 编号。我想为每个模式(mode_0,mode_1,...)创建单独的文件,每个文件中包含与该模式对应的一千个左右的值。我可以使用第一个 awk 调用创建文件,但无法将相应模式幅度值的数千个左右记录放入第一个 awk 调用创建的文件中。一次不成功的尝试是第二次 awk 调用。

    ########## MODE NUMBER is "  0"     (RAYLEIGH WAVE) ##########
I    DEPTH          Y1             Y2             Y3             Y4
1   3.000000E-01   9.999983E-01   1.166993E+06  -1.280462E-02   0.000000E+00
2   6.000000E-01   9.999933E-01   2.351593E+06  -2.580244E-02   0.000000E+00
     This continues for a thousand or so records.
-1  0.000000E+00   0.000000E+00   0.000000E+00   0.000000E+00   0.000000E+00

    ########## MODE NUMBER is "  1"     (RAYLEIGH WAVE) ##########
I    DEPTH          Y1             Y2             Y3             Y4        1   3.000000E-01   9.999960E-01   1.183126E+06  -1.280343E-02   0.000000E+00
2   6.000000E-01   9.999840E-01   2.367720E+06  -2.562274E-02   0.000000E+00
         This continues for a thousand or so records.
-1  0.000000E+00   0.000000E+00   0.000000E+00   0.000000E+00   0.000000E+00

单个模型的最后一行始终以最后一条记录的第一个字段中的 -1 结尾。记录数是可变的,通常为 1000 条或更多记录。然后下一个模式以与前一个模式完全相同的格式开始,从第三个记录的第一个字段中的 1 开始,到该模式的最后一个记录的第一个字段中以 -1 结束。

我一直在努力做的是:

  1. 为每个模式创建一个单独的文件,标记为 mode_0、mode_1、mode_2、...、mode_N。
  2. 将模式幅度值写入相应的 mode_n 文件。这些值是“I DEPTH ...”标签下方的浮点数。

我对 awk 非常缺乏经验,正如您从下面我最近的尝试中看到的那样。该示例共有 5 种模式,即 mode_0 到 mode_4。对 awk 的第一次调用按预期工作,创建单独的模式文件。第二个 awk 调用是我将值写入各个模式文件的多次失败尝试之一。我还尝试了 awk 范围模式 / 1 /,/ -1 / ,它也不起作用。我试图让第二个 awk 调用仅适用于下面列出的一种模式,但也没有成功。我试图找出如何获取第一行第一个字段中带有“I”的记录和模式幅度值最后一个记录中的第一个字段“-1”之间的所有模式幅度值。尽管模态幅度浮点数可以为负数,但“-1”是严格的整数并且被空格包围,这使其成为搜索每个单独模态幅度值的最后记录的良好模式。

gawk '/MODE / {

if($6 == "0\"" ) $6 = 0 # Remove double quotes from MODE 0" which only occurs for mode 0.
  arr[i] = substr( $6,1,length($6-1))
 {print $0 >> ("mode_"arr[i])}
}' inputfile
gawk '{ for (i = 1 ; i <= 4; i++)
 if ( ( arr[i] == 0 ) &&
    (  $1 == " I " && $1 != " -1 ") )
 print $0 >> ("mode_"arr[i])
}' inputfile

答案1

尝试这个:

gawk '{
  if ($1 == "##########") {
    FS = "\"";
    $0 = $0;
    close(modefile);
    modefile = "mode_"int($2);
    FS = " "
  } else {
    if ($1 != "-1")
      print $0 >> modefile
  }
}' inputfile

答案2

根据你的描述,我认为你关注的是错误的事情。 “MODE NUMBER is”模式是搜索并触发更改输出文件名的内容。其他所有内容都仅打印到当前输出文件中。

下面是如何在 Perl 中做到这一点。

我不确定您是否希望输出中包含“MODE NUMBER is”行(或带有 DEPTH、Y1、Y2、Y3、Y4 的字段标题行),因此我添加了语句,如果您需要,可以取消注释被排除在外。

$ cat split-modes.pl
#!/usr/bin/perl

while (<<>>) {
  # extract mode number, handling optional quote and leading spaces
  if (/MODE NUMBER is "? *(\d+)/i) {
    open($FH, ">", "mode_$1") || die "Couldn't open output file 'mode_$1': $!\n";

    # uncomment next line to exclude the "MODE NUMBER is" line from the output
    #next;
  };
  # Uncomment next line to exclude the "DEPTH Y1..Y4" header line
  #next if (/^\s*I\s/);

  print $FH $_;
}

或者作为稍微简化的“一行”从命令行或 shell 脚本等运行:

perl -n -e '
  if (/MODE NUMBER is "? *(\d+)/i) {
    open($FH, ">", "mode_$1");
    next;
  };
  next if (/^\s*I\s*/);
  print $FH $_;' inputfile

这里有大致相同的算法gawk。主要区别在于提取模式号需要稍微多一些的工作,并且一旦我们不再需要它,在 awk 中显式关闭文件句柄通常是一个好主意(这是由 perl 语句隐式完成的open()) -当您只有五个输出文件时,这并不是绝对必要的,但这是一个值得养成的好习惯。请参阅 gawk 手册部分5.9 关闭输入输出重定向

gawk '
/MODE NUMBER is/ {
  # extract number(s) with 1-or-more digits in MODE line into array "a"
  match($0,/[0-9]+/,a);
  close(out);

  # we are only interested in the first element of a
  out = "mode_" a[0];

  #next;
};

#/^[[:space:]]*I[[:space:]]/ { next };

{ print $0 > out }' inputfile

答案3

听起来这可能就是您所需要的,在每个 Unix 机器上的任何 shell 中使用任何 awk:

awk '/MODE/{close(out); out="mode_"(cnt++)} {print > out}' file

但仅提供 1 条记录作为示例输入,并且没有预期输出,这是未经测试的猜测。

鉴于您更新的示例输入显示它是空行分隔的记录,仅使用 awk 的“段落模式”(仍然使用任何 awk)来处理会更简单:

awk -v RS= '{out="mode_"(NR-1); print > out; close(out)}' file

例如:

$ cat file
    ########## MODE NUMBER is "  0"     (RAYLEIGH WAVE) ##########
I    DEPTH          Y1             Y2             Y3             Y4
1   3.000000E-01   9.999983E-01   1.166993E+06  -1.280462E-02   0.000000E+00
2   6.000000E-01   9.999933E-01   2.351593E+06  -2.580244E-02   0.000000E+00
     This continues for a thousand or so records.
-1  0.000000E+00   0.000000E+00   0.000000E+00   0.000000E+00   0.000000E+00

    ########## MODE NUMBER is "  1"     (RAYLEIGH WAVE) ##########
I    DEPTH          Y1             Y2             Y3             Y4        1   3.000000E-01   9.999960E-01   1.183126E+06  -1.280343E-02   0.000000E+00
2   6.000000E-01   9.999840E-01   2.367720E+06  -2.562274E-02   0.000000E+00
         This continues for a thousand or so records.
-1  0.000000E+00   0.000000E+00   0.000000E+00   0.000000E+00   0.000000E+00

$ awk -v RS= '{out="mode_"(NR-1); print > out; close(out)}' file

$ head mode_*
==> mode_0 <==
    ########## MODE NUMBER is "  0"     (RAYLEIGH WAVE) ##########
I    DEPTH          Y1             Y2             Y3             Y4
1   3.000000E-01   9.999983E-01   1.166993E+06  -1.280462E-02   0.000000E+00
2   6.000000E-01   9.999933E-01   2.351593E+06  -2.580244E-02   0.000000E+00
     This continues for a thousand or so records.
-1  0.000000E+00   0.000000E+00   0.000000E+00   0.000000E+00   0.000000E+00

==> mode_1 <==
    ########## MODE NUMBER is "  1"     (RAYLEIGH WAVE) ##########
I    DEPTH          Y1             Y2             Y3             Y4        1   3.000000E-01   9.999960E-01   1.183126E+06  -1.280343E-02   0.000000E+00
2   6.000000E-01   9.999840E-01   2.367720E+06  -2.562274E-02   0.000000E+00
         This continues for a thousand or so records.
-1  0.000000E+00   0.000000E+00   0.000000E+00   0.000000E+00   0.000000E+00

答案4

我终于解决了这个问题。以下非常简单的脚本正是我所需要的:

  • 第一个模式搜索包含模式编号的记录。
  • 第二个范围模式恰好将写入文件中的幅度值的开头和结尾括起来,该文件的名称中包含模式编号。
  • 模式中的空格可确保抓取正确的记录。
gawk '/MODE /{ if ($6 == "0\"" ) $6 = 0
    modenum = substr( $6,1,length($6-1)) 
    close(modefile)
    modefile = "mode_"modenum
}
/ I  /,/ -1  /{
    print $0 >> modefile
}' infile

相关内容