我有一个包含数千行的文件,描述弹性波模式代码的输出。文件的大小根据频率、模型的厚度以及为特定模型找到的模式数量而变化。标头信息包含找到的模式数量。模式从 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 结束。
我一直在努力做的是:
- 为每个模式创建一个单独的文件,标记为 mode_0、mode_1、mode_2、...、mode_N。
- 将模式幅度值写入相应的 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