sed / awk / perl удаляет первые две строки трехстрочного шаблона - PullRequest
3 голосов
/ 24 сентября 2019

У меня огромный текстовый файл.Мне нужно заменить все вхождения этого трехстрочного шаблона:

|pattern|some data|
|giberish|,,
|pattern|some other data|

последней строкой шаблона:

|pattern|some other data|

удалить первые две строки шаблона,оставьте только последний .

  • Вторая строка шаблона заканчивается двумя запятыми и не начинается с |pattern|
  • Первая строка строки шаблона начинаетсяс |pattern| и не заканчивается двумя запятыми.
  • Третья строка строки шаблона начинается с |pattern| и не заканчивается двумя запятыми.

Я пробовал это:

sed 'N;N;/^|pattern|.*\n.*,,\n|pattern|.*/I,+1 d' trial.txt

без особой удачи

Редактировать: Вот более существенный пример

#!/usr/bin/env bash
cat > trial.txt <<EOL
|pattern|sdkssd|
|.x,mz|e,dsa|,,
|pattern|sdk;sd|
|xl'x|cxm;s|,,
|pattern|aslkaa|
|l'kk|3lke|,,
|x;;lkaa|c,c,s|
|-0-ses|3dsd|
|xk;xzz|'l3ld|
|0=9c09s|klkl32|
|d0-zox|m,3,a|
|x'.za|wkl;3|
|=-0poxz|3kls|
|x-]0';a|sd;ks|
|wsd|756|
|sdw|;lksd|
|pattern|askjkas|
|xp]o]xa|lk3j2|,,
|]-p[z|lks|
EOL

, и он должен стать:

|pattern|aslkaa|
|l'kk|3lke|,,
|x;;lkaa|c,c,s|
|-0-ses|3dsd|
|xk;xzz|'l3ld|
|0=9c09s|klkl32|
|d0-zox|m,3,a|
|x'.za|wkl;3|
|=-0poxz|3kls|
|x-]0';a|sd;ks|
|wsd|756|
|sdw|;lksd|
|pattern|askjkas|
|xp]o]xa|lk3j2|,,
|]-p[z|lks|

@ zdim:

первые три строки файла:

|pattern|sdkssd|
|.x,mz|e,dsa|,,
|pattern|sdk;sd|

удовлетворяют шаблону.Таким образом, они заменяются на

|pattern|sdk;sd|

, поэтому верхняя часть файла теперь становится:

|pattern|sdk;sd|
|xl'x|cxm;s|,,
|pattern|aslkaa|
|l'kk|3lke|,,
...

, первые три строки которых:

|pattern|sdk;sd|
|xl'x|cxm;s|,,
|pattern|aslkaa|

, которыеудовлетворяют шаблону, поэтому они заменяются на:

|pattern|aslkaa|

, поэтому верхняя часть файла теперь выглядит так:этот файл:

#!/usr/bin/env bash
cat > trial.txt <<EOL
|pattern|blabla|
|||4|||-0.97|0|1429037262.8271||20160229||1025||1000.0|0.01|,,
|pattern|blable|
|||5|||-1.27|0|1429037262.854||20160229||1025||1000.0|0.01|,,
|pattern|blasbla|
|||493|||-0.22|5|1429037262.8676||20170228||1025||1000.0|0.01|,,
|||11|||-0.22|5|1429037262.8676||20170228||1025||1000.0|0.01|,|T|347||1429043438.1962|-0.22|5|0||-0.22|1429043438.1962|,|Q|346||1429043437.713|-0.24|26|-0.22|5|||1429043437.713|
|pattern|jksds|
|||232|||-5.66|0|1429037262.817||20150415||1025||1000.0|0.01|,,
|pattern|bdjkds|
|||123q|||-7.15|0|1429037262.8271||20150415||1025||1000.0|0.01|,,
|pattern|blabla|
|||239ps|||-1.38|79086|1429037262.8773||20150415||1025||1000.0|0.01|,,
|||-92opa|||-1.38|79086|1429037262.8773||20150415||1025||1000.0|0.01|,|T|1||1428969600.5019|-0.99|1|11||||,
|||kj2w|||-1.38|79086|1429037262.8773||20150415||1025||1000.0|0.01|,|T|2||1428969600.5019|-1|1|11||||,
|||0293|||-1.38|79086|1429037262.8773||20150415||1025||1000.0|0.01|,|T|3||1428969600.5019|-1.01|1|11||||,
|||2;;w32|||-1.38|79086|1429037262.8773||20150415||1025||1000.0|0.01|,|T|4||1428969600.5019|-1.11|1|11||||,
EOL

Ответы [ 5 ]

2 голосов
/ 25 сентября 2019

Вот простой пример использования буфера для сбора и управления строками паттернов

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

my $file = shift or die "Usage: $0 file\n";

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

my @buf;

while (<$fh>) { 
    chomp;
    if (/^\|pattern\|/ and not /,,$/) { 
        @buf = $_;        # start the buffer (first line) or overwrite (third)
    }   
    elsif (/,,$/ and not /^\|pattern\|/) { 
        if  (@buf) { push @buf, $_ }  # add to buffer with first line in it
        else       { say }            # not part of 3-line-pattern; print
    }   
    else { 
        say for @buf;  # time to print out buffer
        @buf = ();     # ... empty it ...
        say            # and print the current line
    }   
}

Это печатает ожидаемый результат.

Объяснение.

  • Строки-паттерны идут в буфер, и когда мы получаем «третью строку», необходимо удалить первые две;так что мы можем просто «присвоить» массиву всякий раз, когда мы видим ^|pattern| - либо запустить буфер, если это первая строка, либо повторно инициализировать массив (удалив то, что в нем), если это третья строка

  • Строка, заканчивающаяся ,,, добавляется в буфер, если там уже есть строка;ничто не запрещает линии, заканчивающиеся на ,,, просто так - они могут быть вне шаблона;в этом случае просто выведите

  • Таким образом, каждая строка |pattern| устанавливает буфер прямо - либо запускает его, либо сбрасывает его.Таким образом, как только мы столкнемся со строкой, не содержащей ни ^|pattern|, ни ,,$, мы сможем распечатать наш буфер, и эту строку

Пожалуйста, проверьте более подробно, что я до сих пор не получилсделать.


Чтобы выполнить это либо в конвейере, либо в файле, используйте файловый дескриптор "magical" <>.Таким образом, он становится

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

my @buf;

while (<>) {  # reads lines from either STDIN or files given on command line
    ...
}

Теперь вы можете запустить его как data | script.pl или как script.pl datafile.(Сделайте для этого исполняемый скрипт или используйте его как perl script.pl.)

Выходные данные скрипта отправляются в STDOUT, который может быть передан в другие программы или перенаправлен в файл.

1 голос
/ 25 сентября 2019

Это может работать для вас (GNU sed):

sed ':a;N;s/[^\n]*/&/3;Ta;/^|pattern|.*\n.*,,\n|pattern|/{/,,\n.*\n\|,,$/!{s/.*\n//;ba}};P;D' file

Заполните пространство шаблона следующими тремя строками файла.Если первый шаблон соответствует текущим трем строкам, и ни первая, ни третья строки не заканчиваются на ,,, удалите первые две строки и повторите.В противном случае выведите и удалите первую строку трехстрочного окна и повторите.

1 голос
/ 25 сентября 2019

Решение awk:

awk -v pa=pattern '
    $0 ~ pa {
        do {
            hold=$0;
            getline;
            hold=hold "\n" $0;
            getline;
        } while(match($0, pa));
        print hold
    }
    1' trial.txt

Идея состоит в том, чтобы буферизовать строку, которая соответствует шаблону, а затем строку после.Если следующая строка также соответствует шаблону, цикл, это время буферизует самую последнюю совпадающую строку и и ту, которая следует за ней.Это приводит к удалению строк, которые необходимо заменить.

Когда цикл останавливается, первая строка, содержащаяся в буфере, это либо строка для замены удаленных строк, либо просто первое совпадение с образцом, которое неудалить.В любом случае содержимое буфера будет напечатано.

Последний оператор 1 необходим для печати строки, завершившей цикл while, и всех других строк, которые не являются первой или второй после той, которая соответствует шаблону.

1 голос
/ 25 сентября 2019

Это может зависеть от размера вашего файла, но если он меньше разрешенного объема памяти, как насчет:

perl -0777 -pe '
    1 while s/^\|pattern\|.+?\|\n(?<!\|pattern\|).+?,,\n(\|pattern\|.+?\|)$/\1/m;
' trial.txt

Вывод:

|pattern|aslkaa|
|l'kk|3lke|,,
|x;;lkaa|c,c,s|
|-0-ses|3dsd|
|xk;xzz|'l3ld|
|0=9c09s|klkl32|
|d0-zox|m,3,a|
|x'.za|wkl;3|
|=-0poxz|3kls|
|x-]0';a|sd;ks|
|wsd|756|
|sdw|;lksd|
|pattern|askjkas|
|xp]o]xa|lk3j2|,,
|]-p[z|lks|
1 голос
/ 24 сентября 2019

Обновленный ответ: Должно работать следующее решение sed :

  sed '/\n/!N;/\n.*\n/!N;/^|pattern|.*\n.*,,\n|pattern|/!{P;D;};s/[^\n]*\n//;D;'

Объяснение:

  • /\n/!N, если в P-пространстве есть только одна строка, читать следующую
  • /\n.*\n/!N, если в P-пространстве есть только две строки, читать в третьем
  • /^|pattern|.*\n.*,,\n|pattern|/ тестеесли первая и третья строки начинаются с | pattern |, а средняя строка заканчивается двумя запятыми
  • !{P;D;}, если совпадение не удается , выведите первую строку и начните с
  • s/[^\n]*\n//;D; в противном случае, когда совпадение завершится успешно , удалите первые две строки и начните заново.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...