Shell – sed: multi-line replace of config block

sedshell-scripttext processing

I have some configuration files that basically look like

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

Now, in bash using CONTENT=`wget -O - http://$SERVER/get_config.php`, I have the replacement for the dynamic block.

How do I do the replace now and how do I make the script insert the block at file's end if it isn't there?

Best Answer

If you want to use sed, you can read from a named pipe. Beware that this code doesn't try to cope with errors. The script will block if the dynamic block header is present more than once.

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$/ {'
          -e p -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"

But I'd go for 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";
        }
    }
'