sed: многострочная замена блока конфигурации

У меня есть некоторые файлы конфигурации, которые в основном выглядят

(...content...) # BEGIN DYNAMIC BLOCK - DO NOT EDIT MANUALLY (... more content ...) # END DYNAMIC BLOCK (... even more content ...) 

Теперь, в bash, используя CONTENT=`wget -O - http://$SERVER/get_config.php` , у меня есть замена для динамического блока.

Как сделать замену сейчас и как заставить скрипт вставить блок в конец файла, если его нет?

3 Solutions collect form web for “sed: многострочная замена блока конфигурации”

Если вы хотите использовать sed, вы можете читать из именованного канала. Помните, что этот код не пытается справиться с ошибками. Сценарий блокируется, если заголовок динамического блока присутствует несколько раз.

 CONTENT_URL="http://$SERVER/get_config.php" tmp=$(mktemp -d) ( cd "$tmp" mkfifo dynamic_seen dynamic_content : >dynamic_seen & seen_pid=$! wget -O dynamic_content "$CONTENT_URL" & wget_pid=$! sed -e '/^# BEGIN DYNAMIC BLOCK - DO NOT EDIT MANUALLY$/ p' \ -e '/^# END DYNAMIC BLOCK$/ {' -ep -e 'r dynamic_seen' -e 'r dynamic_content' -e '}' \ -e '/^# BEGIN DYNAMIC BLOCK - DO NOT EDIT MANUALLY$/, /^# END DYNAMIC BLOCK$/ d' if ! kill $dynamic_seen 2>/dev/null; then # The pipe hasn't been read, so there was no dynamic block. Add one. echo "# BEGIN DYNAMIC BLOCK - DO NOT EDIT MANUALLY" cat dynamic_pipe echo "# END DYNAMIC BLOCK - DO NOT EDIT MANUALLY" fi ) rm -rf "$tmp" 

Но я пошел бы на awk.

 export CONTENT_URL="http://$SERVER/get_config.php" awk ' $0 == "# END DYNAMIC BLOCK - DO NOT EDIT MANUALLY" {skip=0; system("wget \"$CONTENT_URL\""); substituted=1} !skip {print} $0 == "# BEGIN DYNAMIC BLOCK - DO NOT EDIT MANUALLY" {skip=1} END { if (!substituted) { print "# BEGIN DYNAMIC BLOCK - DO NOT EDIT MANUALLY"; system("wget \"$CONTENT_URL\""); print "# END DYNAMIC BLOCK - DO NOT EDIT MANUALLY"; } } ' 

Я бы пошел с суб-оболочкой и двумя командами sed, примерно так:

 beg_tag='# BEGIN DYNAMIC BLOCK - DO NOT EDIT MANUALLY' end_tag='# END DYNAMIC BLOCK' ( sed "/^$beg_tag"'$/,$d' oldconf echo "$beg_tag" wget -O - http://$SERVER/get_config.php echo "$end_tag" sed "1,/^$end_tag/d" oldconf ) > newconf 

Будьте осторожны, чтобы не beg_tag знаковых символов в beg_tag и end_tag .

Это добавит результат, если теги отсутствуют. Первая команда sed никогда не удалит строки из ввода, а вторая команда sed удалит все строки.

тестирование

Если oldconf содержит:

 (...content...) # BEGIN DYNAMIC BLOCK - DO NOT EDIT MANUALLY (... more content ...) # END DYNAMIC BLOCK (... even more content ...) 

И команда wget заменяется echo hello world , вывод:

 (...content...) # BEGIN DYNAMIC BLOCK - DO NOT EDIT MANUALLY hello world # END DYNAMIC BLOCK (... even more content ...) 

Теперь, если блок удален, т. Е. Используется следующий вход:

 (...content...) (... even more content ...) 

Выход:

 (...content...) (... even more content ...) # BEGIN DYNAMIC BLOCK - DO NOT EDIT MANUALLY hello world # END DYNAMIC BLOCK 

Это довольно просто сделать с sed . Вам просто нужно балансировать диапазоны линий друг против друга и привязываться к EOF.

 INPUT | sed -e 's/\\/&&/g;$!s/$/\\/' | #this sed escapes INPUT for scripting sed -e '/^'"$START"'/,$!{$!b #this sed applies concatenated scripts G;G;s/$/'"$END"'/;P;:n };$!N; /\n'"$END"'/,$!{G;$!bn }; /\n\n/c\' -f - -e 'P;$d;D ' ./named_infile >outfile 

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

 /^$START/,$!{ -- function --} N; /\n$END/,$!{ -- function -- } 

Идея состоит в том, что когда мы привязываем линейный диапазон к одной из строк 1 или $ EOF, мы по существу просто сделали его жадным . Обычно линейные диапазоны применяются только к наименьшему подмножеству строк, к которым они могут – начинаются заново для каждого соответствия LHS, и заканчиваются для самого первого совпадения RHS, которое затем происходит во входе. Если RHS является EOF, хотя, ну, их можно применять только один раз – потому что есть только один из них.

Когда я делаю:

 /^$START/,$!{ -- function -- } 

Я указываю, что весь код между коленями выполняется для каждой строки в infile до, но не включая $START . В этом контексте функции я учусь за каждую строку ! а не $ last.

Таким образом, все строки до первого ввода $START печатаются автоматически и игнорируются, но если последняя строка попадает в этот диапазон – как это может быть, если $START никогда не встречается ни разу – тогда он готов к тому, чтобы быть повешенным ваша строка.

И поэтому, если ваш диапазон не встречается во входе, INPUT добавляется в конец файла.

Когда я буду делать следующее:

 N; /\n$END/,$!{ -- function -- } 

Я снова применяю функцию контекстно. На этот раз он применяется к телу вашего диапазона – и единственное первое вхождение в него – потому что дополнение /\n$END/,$ – это все строки, которые не были распущены до первого $START , и только до и не включая следующий встречный $END .

В этом случае применяемая функция представляет собой цикл ветвления – пока вход попадает в этот диапазон, он будет продолжать возвращаться назад и втягивать строку N ext до тех пор, пока не найдет первое совпадение $END , после чего он c повесит всю диапазон до содержимого -f - файла сценария stdin – или вашего экранированного ввода. Это же правило применяется к последней строке в случае, если оно встречается до первого совпадения $START .

Вот и все. Обратите внимание, что это не требует каких-либо специальных файлов для работы – потому что он (безопасно) включает в себя копию INPUT в своем скрипте, поэтому в любое время не нужно применять его при необходимости.

  • Узнайте, на какой строке в текстовом файле соответствует слово
  • Вставьте новые строки с отсутствующими значениями (NA)
  • Зацикливание файлов с пробелами в именах?
  • Извлечение URL из неформатированного текста
  • Как захватить содержимое строки до определенной строки?
  • Grep для строки, а затем повторить строку из записи
  • Печать регулярного выражения Sed в файл .txt
  • Вставить текст в определенные строки файла?
  • grep -f patternfile не находит ничего или слишком сильно в зависимости от содержимого шаблона
  • Лучше, чем `tee | разрезать | ... | paste`
  • Сохранять вывод из одной команды и обрабатывать ее для другого
  • Linux и Unix - лучшая ОС в мире.