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` , у меня есть замена для динамического блока.

  • Заменить соответствующие круглые скобки с содержимым
  • найти и заменить строку в файле без использования временного файла с SED
  • Многострочный grep был связан с двумя тегами, отображать только последнее совпадение
  • Печатать текст между тегами (включительно), если обнаружен определенный текст
  • Сопоставление и удаление строк с ~ 100 строк
  • Заменить «o» на «0» в большом списке слов и сохранить исходное слово?
  • Как сделать замену сейчас и как заставить скрипт вставить блок в конец файла, если его нет?

  • Перемещение одного файла за раз, исходя из определенного числа в файлах
  • Выход grep cdrecord
  • Sed: как заменить символ nextline \ n в текстовых файлах?
  • Удалите блок строк с условием на последней строке
  • Найдите каждую строку, соответствующую шаблону, но напечатайте только строку над ней
  • Удалите строки на основе шаблона, но сохраняя первые n строк, которые соответствуют
  • 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 в своем скрипте, поэтому в любое время не нужно применять его при необходимости.

    Linux и Unix - лучшая ОС в мире.