Given are two files, the first is an Apache config file:
$ cat vhosts-ssl.conf
<VirtualHost *:443>
vhost 1
foobar 1
foobar 2
barfoo 1
barfoo 2
</VirtualHost>
<VirtualHost *:443>
vhost 2
foobar 2
barfoo 1
foobar 1
barfoo 2
</VirtualHost>
<VirtualHost *:443>
vhost 3
foobar 1
barfoo 1
foobar 2
barfoo 2
</VirtualHost>
<VirtualHost *:443>
vhost 4
foobar 1
foobar 2
barfoo 1
barfoo 2
</VirtualHost>
And the second file contains lines that should be added to the end of one (variable) specific VirtualHost block:
$ cat inserted.txt
inserted line 1
inserted line 2
The result should look like this:
$ cat vhosts-ssl.conf
<VirtualHost *:443>
vhost 1
foobar 1
foobar 2
barfoo 1
barfoo 2
</VirtualHost>
<VirtualHost *:443>
vhost 2
foobar 2
barfoo 1
foobar 1
barfoo 2
inserted line 1
inserted line 2
</VirtualHost>
<VirtualHost *:443>
vhost 3
foobar 1
barfoo 1
foobar 2
barfoo 2
</VirtualHost>
<VirtualHost *:443>
vhost 4
foobar 1
foobar 2
barfoo 1
barfoo 2
</VirtualHost>
I tried it with some variations of the following sed but that didn't do the trick:
$ sed -e '/^<VirtualHost/{:a;n;/^<\/VirtualHost/\!ba;r inserted.txt' -e '}' vhosts-ssl.conf
I can't figure out how to select only the one VirtualHost block i need to insert the file to and since i've to use FreeBSD sed (or awk) i also get this error with previous sed command:
$ sed -e '/^<VirtualHost/{:a;n;/^<\/VirtualHost/\!ba;r inserted.txt' -e '}' vhosts-ssl.conf
sed: 2: "}
": unused label 'a;n;/^<\/VirtualHost/!ba;r inserted.txt'
With GNU sed i get the this output:
$ gsed -e '/^<VirtualHost/{:a;n;/^<\/VirtualHost/\!ba;r inserted.txt' -e '}' vhosts-ssl.conf
<VirtualHost *:443>
vhost 1
foobar 1
foobar 2
barfoo 1
barfoo 2
</VirtualHost>
inserted line 1
inserted line 2
<VirtualHost *:443>
vhost 2
foobar 2
barfoo 1
foobar 1
barfoo 2
</VirtualHost>
inserted line 1
inserted line 2
<VirtualHost *:443>
vhost 3
foobar 1
barfoo 1
foobar 2
barfoo 2
</VirtualHost>
inserted line 1
inserted line 2
<VirtualHost *:443>
vhost 4
foobar 1
foobar 2
barfoo 1
barfoo 2
</VirtualHost>
inserted line 1
inserted line 2
As i would like to understand my mistakes and lern from them, i would prefer answers with some explanation and maybe even some links to rtfm, thanks.
Added 2016-10-16
Pseudocode:
if BLOCK begins with /^<VirtualHost/
and ends with /^<\/VirtualHost/
and is the ${n-th} BLOCK
in FILE_1
then insert content of FILE_2
before last line of ${n-th} BLOCK
without touching rest of FILE_1
endif
save modified FILE_1
The ${n-th} is gathered by:
$ httpd -t -D DUMP_VHOSTS | \
grep -i "${SUBDOMAIN}.${DOMAIN}" | \
awk '/^[^\ ]*:443[\ ]*/ {print $3}' | \
sed -e 's|(\(.*\))|\1|' | \
cut -d: -f2
Output is a the number of the BLOCK i want to extend by FILE_2
And please only non-GNU versions as i'm on FreeBSD, thanks.
In GNU
sed
(and BusyBoxsed
) the file/label/text after thea
,b
,c
,i
,r
,t
,w
, and:
commands can be delimited by a semicolon, while in other versions ofsed
the file/label/text may only be delimited by a newline.That behavior means that instead of defining the label
a
, the first string defines the labela;n;/^<\/VirtualHost/\!ba;r inserted.txt
, and like the separate use of-e
for the closing brace, the script must be separated after both the label and the branch.(also, the
!
must not be escaped)Alternatively, the script can span multiple lines:
Note that this splitting may not work in situations where the newline must be escaped; for example, when using the
a
,c
, andi
commands.awk
to the rescue!requires multi-char record separator, supported by
gawk
read the first file in complete and assign to the variable insert, while iterating the second file at the end of second record print the variable after the record contents.
Another version for plain
awk
Given:
You can do: