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, awk
  • Добавление строки в файл из удаленного ssh
  • Сценарий Bash - части chomp из строки
  • Как выбрать последнее число в строке в сценарии bash
  • Как я могу извлечь числа в файле с помощью sed или любого другого инструмента?
  • Декодирование кодировки URL (процентное кодирование)
  • Извлечь третью группу текста из разделителей диапазонов
  • 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 - лучшая ОС в мире.