【问题标题】:Find regex, move the next line at the end of this line and copy the first 5 columns to the next lines that start with a letter找到正则表达式,将下一行移到这一行的末尾并将前5列复制到以字母开头的下一行
【发布时间】:2019-03-03 20:03:38
【问题描述】:

我有这样的文字:

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 列?

假设我有这 3 行:

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

我想得到:

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

【问题讨论】:

标签: perl awk sed text-processing


【解决方案1】:

下面的 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 -------------- 没有aaa 40 0 -------------- 没有 bbb xxx zzy 40 0 -------------- 没有 bbb aa bb cc 42 2 -------------- 无 ccc xxx zyz 42 2 -------------- 无 ccc a b c d 43 3 -------------- 无 ddd xy zz 43 3 -------------- 无 ddd a a 43 3 -------------- 无 ddd a a 43 3 -------------- 无 ddd c 52 5 -------------- 没有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 -------------- 无 ddd a b c 43 3 -------------- 无 ddd d e f 43 3 -------------- 无 ddd g h i 43 3 -------------- 无 ddd j k

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

37 7 -------------- 没有 aaa 更多列 40 0 -------------- 没有 bbb xxx zzy 啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊 42 2 -------------- 无ccc xxx zyz A B C D 43 3 -------------- 没有 ddd 还有更多 xyz 一个 一个 C a b c d e f g h i j k 52 5 -------------- 没有eee yyyx zzz

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

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

【讨论】:

    【解决方案2】:

    你可以使用下面提到的这个命令,希望它会有所帮助

    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 } }'
    

    【讨论】:

    • 问题是它应该以字母开头的第一行附加到上面以数字开头的行。
    【解决方案3】:

    这可能对你有用(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
    

    【讨论】:

    • 它没有改变输出,我使用的是 sed (GNU sed) 4.2.2。
    • 有效!谢谢!你能解释一下它是如何工作的吗?
    猜你喜欢
    • 2019-03-03
    • 1970-01-01
    • 1970-01-01
    • 2021-09-19
    • 1970-01-01
    • 2013-05-12
    • 1970-01-01
    • 2013-11-15
    • 1970-01-01
    相关资源
    最近更新 更多