redhotpenguin has asked for the wisdom of the Perl Monks concerning the following question:

Oh wise monks,

I seek to increase my regular expression foo and ask for your guidance in streamlining this snippet I wrote to split up a file I have containing VirtualHost entries for Apache. In agreeing to host my friends' websites I have grown lazy and accumulated many VirtualHosts in one single file. Now is the time to clean up my act. Here's what I have which works, I ask your teachings in how to make this code more elegant.

#!/usr/bin/perl use strict; use warnings; open(FH, '< vhosts.conf'); my $content = do {local $/; <FH> }; my $start = q{<VirtualHost \*:80>}; my $finish = q{</VirtualHost>}; my @vhosts = $content =~ m!$start(.*?)$finish!sg; foreach my $vhost (@vhosts) { my ($name) = $vhost =~ /ServerName\s(\S+)/; open(OUT, "> $name.conf") or die "Can't open outfile $name: $!\n"; print OUT "<VirtualHost *:80>"; print OUT $vhost; print OUT "</VirtualHost>"; close(OUT); } 1;

For those not familiar with the data I'm trying to split up here's an example:

<VirtualHost *:80> ServerName www.foobar.com ServerAlias foobar.com DocumentRoot /var/www/foobar.com <Directory /var/www/foobar.com> Order allow,deny Allow from all </Directory> LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i -> %U\" \"%{Use +r-Agent}i\ "" combined ErrorLog /var/log/apache2/foobar.com.error_log CustomLog /var/log/apache2/foobar.com.access_log combined Include conf/deflate.conf </VirtualHost>

Replies are listed 'Best First'.
Re: Clean up httpd.conf virtual host mess with perl
by Cody Pendant (Prior) on Apr 06, 2005 at 10:51 UTC
    Someone has to do it ... here's a highly streamlined version.

    use Apache::ConfigFile... etc.



    ($_='kkvvttuubbooppuuiiffssqqffssmmiibbddllffss')
    =~y~b-v~a-z~s; print
Re: Clean up httpd.conf virtual host mess with perl
by tlm (Prior) on Apr 06, 2005 at 09:39 UTC

    Your code looks fine to me. "Elegance" often gets translated to "succinctness", which can often lead to decreasing legibility. (Verboseness can also be hard to read, but your code doesn't suffer from this.)

    That said, if you want to save a couple of keystrokes without getting too obscure, you could try

    my $content = do { local $/; local @ARGV = 'vhosts.conf'; <> };
    (Actually, that probably didn't save any keystrokes; just avoided creating a new handle.) Also, if you want to use $start and $finish to build your regexp, I would use qr on them:
    my $start = qr{<VirtualHost \*:80>}; my $finish = qr{</VirtualHost>};
    And, in this case, I see nothing wrong with putting all your printing in one line:
    print OUT "<VirtualHost *:80>$vhost</VirtualHost>";
    Lastly, if you want to be excruciatingly correct, you should check the return value of all system calls, and that includes not just open but also close. (But, in this case, maybe you should avoid the ARGV trick above, and check the return value of that open as well).

    But most of these are just personal preferences; whatever improvement they bring to the code, either in terms of readability or "elegance", is marginal at best.

    the lowliest monk

Re: Clean up httpd.conf virtual host mess with perl
by reasonablekeith (Deacon) on Apr 06, 2005 at 10:30 UTC
    As posted above, I don't see much wrong with your code. One thing I would say though, you do have duplication of the opening and closing virtual host tags. You could fix this by changing your three print lines to look like this...
    print OUT "<VirtualHost *:80>"; print OUT $vhost; print OUT "</VirtualHost>"; -> print OUT "$start$vhost$finish";
    My personal take on the whole thing would be as follows. I don't know if it could be regarded as better though.
    open(FH, '< vhosts.conf'); my $content = do {local $/; <FH> }; while ($content =~ m|(<VirtualHost \*:80>.*?</VirtualHost>)|sg) { my $vhost = $1; my ($name) = $vhost =~ m/ServerName\s(\S+)/; open(OUT, "> $name.conf") or die "Can't open outfile $name: $!\n"; print OUT $vhost; close(OUT); }
Re: Clean up httpd.conf virtual host mess with perl
by nobull (Friar) on Apr 06, 2005 at 19:05 UTC
    I agree with everything Pustular Postulant says but I would add that by moving the capture limits you could avoid needing to reconstrict the VirtualHost stanza header/footer.
    my @vhosts = $content =~ /($start.*?$finish)/sg;
      Ah that's very cool. Thank you all for your feedback. This was a good learning experience.