#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
Примечание. В случае обработки текста между шаблоном и последней строкой встречается фиктивная подпись.