Home > Software engineering >  Insert/remove code block in file inside specific code block bash
Insert/remove code block in file inside specific code block bash

Time:02-21

I'm trying to write a script which dynamically can update my local vhosts file.

Basically the idea is to add/remove a virtual host block dynamically.

  1. generate vhost block dynamically ☑️
  2. insert generated block into desired section of vhosts file ⚠️
  3. script that can remove a virtual host by ServerName ./remove-vhost example.test from section (pending)

My vhosts file looks something like this:

## Definition of all system wide vhosts, e.g.
<VirtualHost *:80>
    DocumentRoot "/Users/sammy/workspace"
    ServerName localhost
</VirtualHost>
# ...

## Virtual hosts inside this section should be added/removed
## dynamically
## GENERATED INCLUDES - DO NOT MODIFY ##
<VirtualHost *:80> # This host was generated automatically
    DocumentRoot "/Users/sammy/workspace/example.com"
    ServerName example.test
</VirtualHost>
# ...
## END GENERATED INCLUDES ##

I tried to figure out how to modify the vhosts file using sed. I'm not very used to work with sed, so any assistance is well appreciated. I tired things like

#!/bin/bash
# ...
VHOSTS_PATH="/usr/local/etc/httpd/extra/httpd-vhosts.conf"
BEGIN_OF_CODEBLOCK="## GENERATED INCLUDES - DO NOT MODIFY ##"
END_OF_CODEBLOCK="## END GENERATED INCLUDES ##"
BLOCK_TO_ADD="
<VirtualHost *:$PORT>
    DocumentRoot \"$WORKING_DIR\"
    ServerName $SERVER_NAME
</VirtualHost>"

sed "/$BEGIN_OF_CODEBLOCK/ { N; s/$BEGIN_OF_CODEBLOCK/$BLOCK_TO_ADD\n&/ }" $VHOSTS_PATH

## breaks with different errors saying e.g
# unescaped newline inside substitute pattern

Who could help me solving this issue?

CodePudding user response:

Using shell variables inside sed commands has various issues.

If you have perl available - it's a simpler option.

You export your shell variables so perl can access them through its %ENV (environment) variable.

export BLOCK_TO_ADD BEGIN_OF_CODEBLOCK
perl -ple '$_ = "$ENV{BLOCK_TO_ADD}\n$_" if /$ENV{BEGIN_OF_CODEBLOCK}/' "$VHOSTS_PATH"

$_ is a perl variable which in this case will contain the "current line"

You can write it using the s/// command if that makes more sense to you:

perl -ple 's/$ENV{BEGIN_OF_CODEBLOCK}/$ENV{BLOCK_TO_ADD}\n$&/ if /$ENV{BEGIN_OF_CODEBLOCK}/' "$VHOSTS_PATH"

& in sed changes to $& in perl.

You can use perl -pi -le to add the -i option.

CodePudding user response:

sed is maybe not the best tool for this. So let's use awk instead. But as you ask for a sed solution see at the end for a sed-based insertion.

Insertion with awk

$ VHOSTS_PATH="/usr/local/etc/httpd/extra/httpd-vhosts.conf"
$ BEGIN_OF_CODEBLOCK="## GENERATED INCLUDES - DO NOT MODIFY ##"
$ BLOCK_TO_ADD="<VirtualHost *:$PORT>
    DocumentRoot \"$WORKING_DIR\"
    ServerName $SERVER_NAME
</VirtualHost>"
$ awk -vb="$BEGIN_OF_CODEBLOCK" -vi="$BLOCK_TO_ADD" '
    {print} $0 == b {print i}' "$VHOSTS_PATH"
...
## GENERATED INCLUDES - DO NOT MODIFY ##
<VirtualHost *:80>
    DocumentRoot "foo"
    ServerName bar
</VirtualHost>
...
## END GENERATED INCLUDES ##

Deletion with awk

Note: this deletes any matching section, even if not in the ## GENERATED INCLUDES... part.

$ VHOSTS_PATH="/usr/local/etc/httpd/extra/httpd-vhosts.conf"
$ SERVER_NAME=bar
$ awk -vs='\\<ServerName[[:space:]] '"$SERVER_NAME"'\\>' '
    /^[[:space:]]*<VirtualHost/,/^[[:space:]]*<\/VirtualHost>/ {
      tmp = tmp $0 "\n"}
    /^[[:space:]]*<\/VirtualHost>/ && tmp {
      if(tmp !~ s) printf("%s", tmp); tmp = ""; next}
    !tmp {print}' "$VHOSTS_PATH"
...
## GENERATED INCLUDES - DO NOT MODIFY ##
<VirtualHost *:80> # This host was generated automatically
    DocumentRoot "/Users/sammy/workspace/example.com"
    ServerName example.test
</VirtualHost>
## END GENERATED INCLUDES ##

sed-based insertion

In such cases it is sometimes easier if you store the sed script itself in a shell variable:

$ VHOSTS_PATH="/usr/local/etc/httpd/extra/httpd-vhosts.conf"
$ BEGIN_OF_CODEBLOCK="## GENERATED INCLUDES - DO NOT MODIFY ##"
$ PORT=80
$ WORKING_DIR=foo
$ SERVER_NAME=bar
$ script='/^'"$BEGIN_OF_CODEBLOCK"'$/a \
<VirtualHost *:'"$PORT"'>\
    DocumentRoot \"'"$WORKING_DIR"'\"\
    ServerName '$SERVER_NAME'\
</VirtualHost>'
$ sed -e "$script" "$VHOSTS_PATH"
## Definition of all system wide vhosts, e.g.
<VirtualHost *:80>
    DocumentRoot "/Users/sammy/workspace"
    ServerName localhost
</VirtualHost>
# ...

## Virtual hosts inside this section should be added/removed
## dynamically
## GENERATED INCLUDES - DO NOT MODIFY ##
<VirtualHost *:80>
    DocumentRoot "foo"
    ServerName bar
</VirtualHost>
<VirtualHost *:80> # This host was generated automatically
    DocumentRoot "/Users/sammy/workspace/example.com"
    ServerName example.test
</VirtualHost>
# ...
## END GENERATED INCLUDES ##
  • Related