结果:

结果:

我有一个文本文件,其中包含以下多列:

 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是范围开始/结束行号。他们的州将通知我们做出正确的决定。

相关内容