查找正则表达式,将下一行移至该行的末尾,然后将前5列复制到以字母开头的下一行

时间:2018-09-27 20:47:31

标签: perl awk sed text-processing

我有这样的文字:

37    7    --------------  No  aaa
40    0    --------------  No  bbb
xxx   zzy
aa    bb   cc
42    2    --------------  No  ccc
xxx   zyz
a     b    c               d
43    3    --------------  No  ddd
xy    zz
a     a
a     a
c
52    5    --------------  No  eee
yyyx  zzz

当我用awk处理它时,我得到:

awk '{if($1+0==$1) p=$1 FS $2 FS $3 FS $4 FS $5; else $0=p FS $0}1' /tmp/test3 | column -t
37  7  --------------  No  aaa
37  7  --------------  No  aaa  xxx   zzz
40  0  --------------  No  bbb
40  0  --------------  No  bbb  xxx   zzy
40  0  --------------  No  bbb  aa    bb   cc
42  2  --------------  No  ccc
42  2  --------------  No  ccc  xxx   zyz
42  2  --------------  No  ccc  a     b    c   d
43  3  --------------  No  ddd
43  3  --------------  No  ddd  xy    zz
43  3  --------------  No  ddd  a     a
43  3  --------------  No  ddd  a     a
43  3  --------------  No  ddd  c
52  5  --------------  No  eee
52  5  --------------  No  eee  yyyx  zzz

我需要获取以下输出:

37    7    --------------  No  aaa
40    0    --------------  No  bbb xxx   zzy
40    0    --------------  No  bbb aa    bb   cc
42    2    --------------  No  ccc xxx   zyz
42    2    --------------  No  ccc a     b    c  d
43    3    --------------  No  ddd xy    zz
43    3    --------------  No  ddd a     a
43    3    --------------  No  ddd a     a
43    3    --------------  No  ddd c
52    5    --------------  No  eee yyyx  zzz

预先感谢您的帮助!我也尝试过 awk '/-/{base=$0; next} {print base, $0}' /tmp/test4 | column -t,如建议的那样,但是如果连续的行以数字开头,则会删除以数字开头的第一行。

更新

这个sed咒语解决了我的问题: sed -r':a; N; / ^ [0-9]。 \ n [0-9] / {P; D} ;: b; s /^(.)\ n( 。)/ \ 1 \ 2 \ n \ 1 /; P; s/。 \ n //; $ d; N; / \ n [0-9] / D; bb'/ tmp / test2

另一个问题:如果我在输出行中有8列以上,是否可以修改sed命令,以便将第9、10和11列移至新行并在其前复制前5列? / p>

假设我有以下3行:

42    2    --------------  No  ccc xxx   zyz
42    2    --------------  No  ccc a     b    c    d    e    f
43    3    --------------  No  ddd xy    zz

我想得到:

42    2    --------------  No  ccc xxx   zyz
42    2    --------------  No  ccc a     b    c
42    2    --------------  No  ccc d     e    f
43    3    --------------  No  ddd xy    zz

3 个答案:

答案 0 :(得分:0)

这可能对您有用(GNU sed):

sed -r ':a;N;s/^(.*)\n\1(.)/\1\2/;ta;P;D' file

打开至少两行的窗口。如果前一行的开头与当前行的头完全相同,并且当前行较长,则删除前一行并重复。否则,打印然后删除第一行并重复。

这是根据awk脚本运行的。

要使用原始数据实现相同的解决方案,请使用:

sed -r ':a;N;/^[0-9].*\n[0-9]/{P;D};:b;s/^(.*)\n(.*)/\1 \2\n\1/;P;s/.*\n//;$d;N;/\n[0-9]/D;bb' file

答案 1 :(得分:0)

以下Perl脚本具有以下要求。

输入包含以数字或非数字开头的交替行块,其中每个数字行块后跟一个文本行块。 已更新:对于输出,需要将最后一个数字行中其块的前五列添加到紧随其后的文本块中的每个文本行之前。其他文本行将按原样打印。

该代码在其缓冲区中收集数字行和文本行。一旦我们到达下一个数字行块的第一行,即两个缓冲区均为非空时,它们便被处理并清空。

use warnings;
use strict;
use feature 'say';

my $file = shift @ARGV || 'default_filename.txt';
die "Usage: $0 file\n" if not $file;

open my $fh, '<', $file or die "Can't open $file: $!";

my (@text, @nums);

while (my $line = <$fh>) {
    chomp $line;
    if ($line =~ /^[^0-9]/) { 
        push @text, $line;
        if (eof) {
            process_buffers(\@nums, \@text);
            last
        }
        next;
    }
    elsif (@nums and @text) {
        process_buffers(\@nums, \@text);
    }

    push @nums, $line;
}

sub process_buffers {
    my ($rnums, $rtext) = @_;

    # Remove last number line from array and take its first five columns
    my @last_num_line_cols = (split ' ', pop @$rnums)[0..4];
    # Print other number lines; all consecutive spaces replaced by tabs
    say for map { s/\s+/\t/gr } @$rnums;

    # Print text lines prepended by five columns of last number line
    foreach my $text_line (@$rtext) {
        say join "\t", @last_num_line_cols, $text_line;
    }   

    @$rtext = ();
    @$rnums = ();
}

处理最后一批数字和文本块需要上述eof的条件,因为没有其他测试可以在最后一行进行。它的位置假定最后一行必须是文本行,这是根据我对需求的假设得出的。

此打印

37      7       --------------  No      aaa
40      0       --------------  No      bbb     xxx   zzy
40      0       --------------  No      bbb     aa    bb   cc
42      2       --------------  No      ccc     xxx   zyz
42      2       --------------  No      ccc     a     b    c               d
43      3       --------------  No      ddd     xy    zz
43      3       --------------  No      ddd     a     a
43      3       --------------  No      ddd     a     a
43      3       --------------  No      ddd     c
52      5       --------------  No      eee     yyyx  zzz

(在标签上对齐,如输入所期望和输出所希望的那样)


更新,如问题更新所述,将输出宽度限制为8列

使用此修改后的处理功能版本

sub process_buffers_fmt {
    my ($rnums, $rtext) = @_;

    my @last_num_line_cols = (split ' ', pop @$rnums)[0..4];
    say for map { s/\s+/\t/gr } @$rnums;

    # Format output lines to 8 columns at most
    foreach my $text_line (@$rtext) {
        my @text_cols = split ' ', $text_line;
        while (my @prn_text_cols = splice @text_cols, 0, 3) {
            say join "\t", @last_num_line_cols, @prn_text_cols;
        }    
    }
    @$rtext = ();
    @$rnums = ();
}

这使用splice一次删除输出的文本的前三列,并用最后一个数字行的(五)列进行打印。这是在while循环中完成的,因此一旦@text_cols被全部处理(打印)后,它将停止。

要测试,我将以下内容添加到输入文件中43 3 ...数字行之后的文本块中

a b c d e f g h i j k

并且主程序的输出获取了这些额外的行

43      3       --------------  No      ddd     a       b       c
43      3       --------------  No      ddd     d       e       f
43      3       --------------  No      ddd     g       h       i
43      3       --------------  No      ddd     j       k

我用来测试所有要求和更新的输入文件是

37    7    --------------  No  aaa MORE COLUMNS
40    0    --------------  No  bbb
xxx   zzy
aa    bb   cc
42    2    --------------  No  ccc 
xxx   zyz
a     b    c               d
43    3    --------------  No  ddd  AND YET MORE
xy    zz
a     a 
a     a 
c
a b c d e f g h i j k
52    5    --------------  No  eee
yyyx  zzz

,程序的输出(带有process_buffers_fmt函数)是

37      7       --------------  No      aaa     MORE    COLUMNS
40      0       --------------  No      bbb     xxx     zzy
40      0       --------------  No      bbb     aa      bb      cc
42      2       --------------  No      ccc     xxx     zyz
42      2       --------------  No      ccc     a       b       c
42      2       --------------  No      ccc     d
43      3       --------------  No      ddd     xy      zz
43      3       --------------  No      ddd     a       a
43      3       --------------  No      ddd     a       a
43      3       --------------  No      ddd     c
43      3       --------------  No      ddd     a       b       c
43      3       --------------  No      ddd     d       e       f
43      3       --------------  No      ddd     g       h       i
43      3       --------------  No      ddd     j       k
52      5       --------------  No      eee     yyyx    zzz

答案 2 :(得分:0)

您可以按如下所述使用此命令,希望对您有所帮助

awk '{if($1+0==$1) p=$1 FS $2 FS $3 FS $4 FS $5; else $0=p FS $0}1' test.txt | sort -k2 | column -t | awk '{ if ($6 >= " ") { print } }'