условная вставка sed между текстом и шаблоном

#sed

Вопрос:

Я хотел бы проанализировать содержимое файла с помощью текстовых блоков и добавить дополнительный разделитель.

Пример хорошего существующего блока:

 %
sometext
- - some signature
 

Пример неисправного существующего блока:

 %
sometext
%
someothertext
 

Что я уже могу сделать, так это идентифицировать шаблон и безоговорочно вставить шаблон, например:

 sed '/%$/ i- -' toto
- -
%
1
- -
%
 

в моем тестовом файле.

Как я могу определить, что строка над % символом является текстовым блоком, и если да, то вставьте - - signature - - строку между текстом и новой строкой подписи?

Полный пример:

 %
good signature is present
- - signature - -
% 
bad signature is no present
%
this is also bad
%
this one is good
- - signature - -
 

должен стать

 %
good signature is present
- - signature - -
% 
bad signature is no present
- - signature - -
%
this is also bad
- - signature - -
%
this one is good
- - signature - -
 

Сами тексты не изменятся.

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

1. Обязательно ли это должно быть sed? do is identify the pattern and insert without condition the pattern like Я не понимаю, не могли бы вы объяснить, что такое «шаблон» и что вы пытаетесь вставить? Что случилось с sometext и someothertext ? Не могли бы вы опубликовать более четкий пример ввода и пример вывода для этого ввода ? Вы хотите вставить что-то в «плохую существующую книгу» или «хорошую существующую книгу»? Какой шаблон вы хотите идентифицировать? dentify text the line over the '%' char is text block «Выше» означает «выше»? Что такое «текстовый блок»?

2. да, потому что он должен быть включен в существующий сценарий sed, когда будет найдено решение.

3. @kamiCuk это не тот выбор, по которому я могу действовать

4. Итак, вы хотите: если есть 3 последовательные строки , и первая строка содержит ровно % , вторые строки содержат буквы и пробелы , а 3-я строка не содержит a - - , то вы хотите вставить между 2-й и 3-й строками строку - - signature - - ? Вы используете GNU sed? У вас есть расширенное регулярное выражение sed?

Ответ №1:

Следующий сценарий:

 #!/bin/sh
cat <<EOF |
%
good signature is present
- - signature - -
% 
bad signature is no present
%
this is also bad
%
this one is good
EOF
sed -E '
    # Last line is a big special - we add to hold buffer first.
    ${
        # Give me functions in sed....    
        # Keep last 2 lines in hold space.
        x; G; s/^.*((n[^n]*){2})$/1/; x;
        # Add the line.
        b ADD;
    }
    # Check if current line does not contain - -
    /^- -/!{ b ADD; }
    
    b NOADD; { : ADD;
        # Check if two last lines match the pattern.
        x; /^n% *n[a-zA-Z ] $/{
            # Last line needs to print pattern space first.
            ${ x; p; x; };
            # Insert the line with signature.
            # Flush hold space.
            s/.*/- - signature - -/; p; s/.*//;
            # Last line exits
            ${ d; };
        }; x;
    }; : NOADD
    
    # Keep last 2 lines in hold space.
    x; G; s/^.*((n[^n]*){2})$/1/; x;
'
 

выходы:

 %
good signature is present
- - signature - -
% 
bad signature is no present
- - signature - -
%
this is also bad
- - signature - -
%
this one is good
- - signature - -
 

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

Обработка последней строки наполовину нарушена и, вероятно, также должна быть исправлена и обработана лучше, что оставлено на усмотрение других.


В качестве альтернативы сохранению состояния внутри буфера удержания, вы можете «сохранить» состояние в текущей позиции потока управления внутри скрипта. Я думаю, какой метод выбрать, является субъективным и зависит от предстоящей работы. Я считаю, что на самом деле здесь все проще:

 sed -E '
    : RESTART
    # Check for %
    /^%/{
        n;
        # Check next line for words.
        /^[a-zA-Z ] $/{
            # If end of line, first print, then we add.
            ${ p; b ADD; }
            n;
            # If something else, we also add.
            /^- -/!{ b ADD; }
            b NOADD; { : ADD;
                # Add the signature.
                x; s/.*/- - signature - -/p; x;
                # Last line already printed - just quit.
                ${ d; }
                # We already read next line above - restart.
                b RESTART
            }; : NOADD
        }
    }
'
 

Ответ №2:

С awk :

 awk -v s='- - signature - -' '
       $0=="%"{if(f) print s; f=1}
       $0==s{f=0} 1; END{if(f) print s}' ip.txt
 
  • f это флаг, который будет установлен, если строка ввода есть % , и снят, если signature найден
  • Если f по-прежнему установлено, когда % произойдет следующее, распечатайте подпись
  • END{if(f) print s} это необходимо, если у последнего блока не было подписи
  • Обратите внимание, что точное сравнение строк используется здесь для проверки входных строк, если есть лишние пробелы, вам придется вместо этого использовать регулярное выражение или сначала позаботиться о лишних пробелах

Используя регулярное выражение вместо сопоставления строк, отрегулируйте регулярное выражение по мере необходимости:

 awk -v s='- - signature - -' '
     /^%/{if(f) print s; f=1}
     /^- - signature/{f=0} 1;
     END{if(f) print s}' ip.txt
 

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

1. это работает не так, как ожидалось, он добавляет строку, даже если она уже есть — — …. — —

2. Как упоминалось в последнем пункте, код проверяет точное совпадение строк, если у вас есть лишние пробелы, он завершится ошибкой

3. Я также добавил версию регулярного выражения

Ответ №3:

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

 sed '1{x;s/^/- - dummy sig - -/;x};/^%/{:a;${G;b};n;/^%/{x;p;x;ba};/^- -/!ba}' file
 

В первой строке создайте фиктивную подпись в пространстве хранения для последующего использования.

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

Решение может быть изменено для использования предыдущей подписи, например:

 sed -e '1{x;s/^/- - dummy sig - -/;x};/^%/{:a;${G;b};n;/^%/{x;p;x;ba};/^- -/!ba;h}' file
 

Примечание. В случае обработки текста между шаблоном и последней строкой встречается фиктивная подпись.