Как удалить 2 последовательные строки только при совпадении обоих шаблонов

#perl #awk #sed

Вопрос:

Давайте рассмотрим этот простой файл:

 {
bla bla
bla bla bla
}
{
bla bla
bla bla bla
}
bla bla
bla bla bla
 

Мне нужно удалить только эти последовательные строки:

 }
{
 

Результат должен быть:

 {
bla bla
bla bla bla
bla bla
bla bla bla
}
bla bla
bla bla bla
 

Я попробовал следующее:

 sed -e '/^}$/,/^{$/d' file
 

К сожалению, последняя закрывающая фигурная скобка и оставшиеся строки были удалены:

 {
bla bla
bla bla bla
bla bla
bla bla bla
 

Есть какие-нибудь предложения?

Я открыт для любого простого решения, включающего другие инструменты, такие как awk/perl/… при необходимости.

Ответ №1:

Вы можете использовать это sed :

 sed '/^}$/ { N; /n{$/ d; }' file

{
bla bla
bla bla bla
bla bla
bla bla bla
}
bla bla
bla bla bla
 

Чтобы сохранить изменения в строке:

 sed -i.bak '/^}$/ { N; /n{$/ d; }' file
 

В качестве альтернативы это awk также сработало бы:

 awk '/^}$/ {p = $0; next} p != "" {if (/^{$/) {p=""; next} $0 = p ORS $0; p = ""} 1' file

{
bla bla
bla bla bla
bla bla
bla bla bla
}
bla bla
bla bla bla
 

Комментарии:

1.Существуют угловые случаи, когда любая из этих команд может не выдавать желаемый результат (хотя в вопросе не было представлено никаких угловых случаев). Например, такая последовательность, как } } { .

Ответ №2:

Решение Perl:

 perl -lne 'undef $prev, next if $prev eq "}" amp;amp; $_ eq "{";
           print $prev if defined $prev;
           $prev = $_;
           END { print $prev if defined $prev}' -- file
 
  • -n считывает входные данные строка за строкой и запускает код для каждой строки.
  • -l удаляет новые строки из входных данных и добавляет их в print .
  • Мы сохраняем предыдущую строку в $prev . Если предыдущая строка была } , а текущая строка есть { , мы забываем предыдущую строку и читаем следующую строку. В противном случае мы печатаем предыдущую строку, если она была (что означает, что мы не печатаем пустую строку после забытой строки). Затем мы сохраняем текущую строку в $prev и повторяем.
  • Эта END деталь необходима для печати последней запомнившейся строки, если таковая имеется.

Ответ №3:

С GNU sed для -z :

 $ sed -z 's/}n{n//' file
{
bla bla
bla bla bla
bla bla
bla bla bla
}
bla bla
bla bla bla
 

С GNU awk для многозначных RS:

 $ awk -v RS='}n{n' -v ORS= '1' file
{
bla bla
bla bla bla
bla bla
bla bla bla
}
bla bla
bla bla bla
 

Ответ №4:

 $ sed '$!N;/^}n{$/d;P;D' file
{
bla bla
bla bla bla
bla bla
bla bla bla
}
bla bla
bla bla bla
 

Этот двухстрочный буфер также будет работать для других входных данных, как в следующем тесте:

 $ cat test
}
}
{
foo
}
$ sed '$!N;/^}n{$/d;P;D' test
}
foo
}
 

Ответ №5:

Также вы можете использовать это awk :

 awk 'BEGIN{RS="";FS=OFS="n"}{print $1,$2,$3,$6,$7,$8,$9,$10}' file
{
bla bla
bla bla bla
bla bla
bla bla bla
}
bla bla
bla bla bla
 

Или с помощью for цикла:

 
awk 'BEGIN{RS="";FS=OFS="n"}{
        for(i=1;i<=NF;i  ) {
                if(i == 4 || i == 5) continue; print $i}
}' file
{
bla bla
bla bla bla
bla bla
bla bla bla
}
bla bla
bla bla bla