我有一个文本文件,其中包含以下多列:
1 102.0184 109.5487 107.3716
2 100.2430 107.5874 108.7558
3 103.9029 105.7406 106.9046
4 102.7495 108.1275 104.4731
5 102.8825 105.6664 107.2141
6 104.5323 108.8850 106.0267
7 103.0479 107.3056 105.5108
8 101.2433 108.6113 107.2551
9 104.4821 108.4339 105.9864
10 101.5879 106.3859 102.825
然后我按以下方式对其进行过滤,例如第二列。
awk '$2<104 {print $1,$2}' file.txt
得到这个:
1 102.0184
2 100.2430
3 103.9029
4 102.7495
5 102.8825
7 103.0479
8 101.2433
10 101.5879
我想在不同的文件中打印第一列中的所有连续范围,因此对于这个示例,我将有三个输出文件:
file_1-5.tmp
1 102.0184
2 100.2430
3 103.9029
4 102.7495
5 102.8825
file_7-8.tmp
7 103.0479
8 101.2433
file_10.tmp
10 101.5879
我怎样才能做到这一点?如何为文件中的所有列扩展此例程?我希望有一个解决此问题的通用解决方案(不依赖于本示例中给出的特定范围),因为我想将其应用于多个文件。
答案1
单行awk
命令:
awk -v P=-1 '$2<104 {if ($1>P+1)N=$1;P=$1;print $1,$2>"file"N".tmp"}'
给出结果(如果你不介意文件名)
==> file1.tmp <==
1 102.0184
2 100.2430
3 103.9029
4 102.7495
5 102.8825
==> file7.tmp <==
7 103.0479
8 101.2433
==> file10.tmp <==
10 101.5879
答案2
下面是一个awk
程序。您可以使用参数中给出的列和阈值:
awk -v column=2 -v threshold=104 '
function save() { if (lines != "") print lines >"file_" first "-" last ".txt" }
! ($column < threshold) {
save()
first = last = lines = ""
next
}
{
if (first == "") first = $1
last = $1
lines = lines $1 OFS $column ORS
}
END { save() }
'
请注意,连续的行将保留在内存中,直到被保存。如果您有数亿行连续行,则应调整此解决方案(将每行保存在临时文件中,然后在满足连续块的最后一行时对其进行重命名)。
答案3
awk '{$0 = $2 < 104 ? NR : RS}1' inputfile |
sed -Ee '
$aq
/./,/^$/!d;/./H;$ba;/^$/ba;d;:a
g;s/.//;s/\n.*\n|\n/,/;x;s/.*//;x
s/(.*),(.*)/&w file_\1_\2.tmp/
/,/!s/.*/&w file_&.tmp/
' | ed -s inputfile -
这里我们使用awk/sed/ed
工具。Awk
首先为所有小于 104 的行生成行号。对于其他行,它打印一个空行。然后Sed
进入并查看从非空行到下一个空行的行范围。并将行号存储在保留中。现在可以找到两种类型的范围:n、m 或普通 n。使用这些我们建立一个ed
命令集,它将这些范围分别转换为:n,mw file_n_m.tmp和file_n.tmp。然后 ed 使用这个动态生成的 ed 脚本立即处理输入文件,将这些行放入其 .tmp 文件中。
这是完成任务的一种方法Perl
:
perl -lane '
BEGIN { $fmt = sprintf q[%s%%s\n%s], (chr 39)x2; }
if ( $F[1] < 104 ) {
push @A, "@F[0,1]";
if ( eof ) {
my $f = join $", q<printf>, $fmt, map(qq[\"$_\"], @A), q[>], (( ! defined $a ) ? qq[file_${.}.tmp] : qq[file_${a}_${.}.tmp]);
system("$f");
} else { $a //= $.; }
} else {
next if ! defined $a;
$b //= $.-1;
my $f = join $", q<printf>, $fmt, map(qq[\"$_\"], @A), q[>], (( $a == $b ) ? qq[file_$b.tmp] : qq[file_${a}_$b.tmp]);
system("$f");
($a, $b, @A) = (undef)x2;
}
' yourfile
结果:
给定输入 ,将创建以下 3 个文件:file_1_5.tmp file_7_8.tmp file_10.tmp
其内容
% 更多 file_1_5.tmp file_7_8.tmp file_10.tmp
::::::::::::::
file_1_5.tmp
::::::::::::::
1 102.0184
2 100.2430
3 103.9029
4 102.7495
5 102.8825
::::::::::::::
file_7_8.tmp
::::::::::::::
7 103.0479
8 101.2433
::::::::::::::
file_10.tmp
::::::::::::::
10 101.5879
解释:
首先是基本的顶层思想:我们密切关注第二个字段是否落后于数字 104。在这种情况下,意味着我们需要打印之前的范围。请记住,对于单长度范围,文件名会相应修改以反映这一点。
另一种情况是,当我们正在累积当前范围时 ($F[1] < 104),请记住,在这样做时,如果我们命中,eof
那么我们现在需要打印范围。
PS:该system
命令是使用动态创建的格式动态创建的,其数据是第一和第二字段,最后根据范围创建.tmp文件名。
$a
和$b
是范围开始/结束行号。他们的州将通知我们做出正确的决定。