Найдите регулярное выражение, переместите следующую строку в конец этой строки и скопируйте первые 5 столбцов в следующие строки, начинающиеся с буквы - PullRequest
0 голосов
/ 27 сентября 2018

У меня есть такой текст:

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 как предложено, но он удаляет первую строку, начинающуюся с цифры, если есть последовательная строка, начинающаяся с цифры.

UPDATE

Это заклинание 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    --------------  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 голосов
/ 28 сентября 2018

Скрипт 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 = ();
}

При этом используется сращивание для удаления первых трех столбцов текстового вывода за раз и печати их с (пятью) столбцами последней числовой строки.Это делается в цикле 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
0 голосов
/ 28 сентября 2018

Вы можете использовать эту команду, как указано ниже, надеюсь, она поможет

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 } }'
0 голосов
/ 28 сентября 2018

Это может сработать для вас (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
...