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

Hello everyone, This is my first post. I've been search through previous posts and several Perl books but I haven't been able to find a solution to my problem. I'm quite new to Perl by the way. Here's the problem. I need to parse a httpd.conf file and remove a VirtualHost block. It looks like this:
<VirtualHost 123.123.123.123> ServerName www.domain.com ... </VirtualHost>
I need to be able to parse the file, find the appropriate ServerName, and then remove that entire VHost block. I'm stuck on this one. If anyone can help I would greatly appreciate it.

Thanks, Brendon

Replies are listed 'Best First'.
Re: Parsing file and removing a section
by arturo (Vicar) on Mar 08, 2001 at 01:15 UTC

    Here's a technique I use: open the original file and a new file (the latter for writing). loop through the original file. If the incoming line matches the pattern you're looking for, set a flag to true. Print the incoming line to the outgoing file unless the flag is set. Then, check to see if the line matches the pattern that ends the block you're cutting out. If so, set the flag back to false. If you don't have an explicit ending line (e.g. here you've got a nice XMLish closing tag, but you might have a file like:

    Server a.b.c.d
     line 1
     line 2
    Server e.f.g.h
     line 1
    

    You can monkey with the inner logic of the loop to deal with such situations, but that's left as an exercise for the reader =)

    open OLD, $oldfile or die; open NEW, "> $oldfile.new" or die; my $flag = 0; while (<OLD>) { $flag =1 if /pattern/; print NEW unless $flag; $flag = 0 if /end_pattern/; } rename ("$oldfile", "$oldfile.old") or die; rename ("$oldfile.new", $oldfile) or die;

    Of course, you'd replace my dies with more helpful messages, and substitute real patterns for mine.

    HTH

    Philosophy can be made out of anything. Or less -- Jerry A. Fodor

      Looks like brendonc needs to see if the _next_ line matches. So maybe
      $flag =1 if /pattern/; print NEW $lastln unless $flag; $lastln = $_; if ($flag and /end_pattern/) { $lastln = ''; $flag = 0; }
      ?

      p
Re: Parsing file and removing a section
by Ido (Hermit) on Mar 08, 2001 at 01:28 UTC
    ($^I,@ARGV)=('.bak','file.txt'); while(<>){ my $c=$_=~/<VirtualHost.*>/..$_=~m!</VirtualHost>!; print unless $c; if($c==2){($ServerName)=/ServerName (.*)$/} }
    Something like that might help...
Re: Parsing file and removing a section
by repson (Chaplain) on Mar 08, 2001 at 12:03 UTC
    perl -ne 'print unless /<VirtualHost 123.123.123.123>/ .. m|</VirtualHost>|;' filename

    You should be able to find out about the .. in perlop.

Re: Parsing file and removing a section
by Masem (Monsignor) on Mar 08, 2001 at 02:47 UTC
    Maybe something like...
    my $line; while ($line = <CONF>) { my $string; # check for start of virtual host block if ( $line =~ /^\s*\<VirtualHost/ ) { $string = $line; #no chomp # read in the enter virtualhost block... do { $line = <CONF>; $string = $string . $line; } until $line =~ /^s*\<\/VirtualHost\>/; # Find the server name $string =~ /ServerName\s+(.*)\s/; # Ignore it if necessary...you can add a number of checks here # including multple servers that you want to ignore. if ( $1 ne "my.domain.here" ) { print <CONF.NEW> $string; } } else { # if not a virtual server line, just pass it through. print <CONF.NEW> $line; } }

    Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain
Re: Parsing file and removing a section
by Rudif (Hermit) on Mar 08, 2001 at 04:51 UTC
    Hi brendonc

    The script below shows one way to parse a file when looking for blocks to modify or excize. It splits the file data into an alternating sequence of block separators and block internals, using the regex that matches both separators. Then it scans the sequence backwards, looking for triplets where block-start, block-internals and block-end match your criteria and it splices out the triplet(s).

    Since I don't have a httpd.conf, I made up data from your posting. As is, the script below removes the block containing ServerNameTwo.

    Be warned that this use of split might be considered scary.<br

    HTH
    Rudif
    #! perl -w use strict; $/ = undef; my $data = <DATA>; my @fields = split /(?<=\n)(<\/?VirtualHost.*>[ \t]*\n)/, $data; printf "sanity check before: %d fields\n", scalar @fields; for (my $i = $#fields-2; $i >= 0; --$i) { if ($fields[$i] =~ m(<VirtualHost.*>[ \t]*\n) && $fields[$i+1] =~ m(ServerNameTwo) && $fields[$i+2] =~ m(<\/VirtualHost>[ \t]*\n) ) { splice @fields, $i, 3; } } printf "sanity check after: %d fields\n", scalar @fields; print join '', @fields; __END__ ### mock httpd.conf file used as test data ### Hello everyone, This is my first post. I've been search through previous posts and several Perl books but I haven't been able to find a solution to my problem. I'm quite new to Perl by the way. Here's the problem. I need to parse a httpd.conf file and remove a VirtualHost block. It looks like this: <VirtualHost 123.123.123.123> ServerNameOne www.domain.com ... </VirtualHost> I need to be able to parse the file, find the appropriate ServerName, and then remove that entire VHost block. I'm stuck on this one. If anyone can help I would greatly appreciate i +t. Thanks, Brendon <VirtualHost 123.123.123.123> ServerNameTwo www.domain.com ... </VirtualHost> qwertzuiop... <SomeOtherBlock> qwertzuiop... </SomeOtherBlock> <VirtualHost 123.123.123.123> ServerNameThree www.domain.com ... </VirtualHost> qwertzuiop... qwertzuiop... qwertzuiop...
      Hi if need to remove multiple domains from a different text file and change
      $fields[$i+1] =~ m(ServerNameTwo)
      into
      $fields[$i+1] =~ m($domainvariable)
      is not working.
Re: Parsing file and removing a section
by brendonc (Novice) on Mar 08, 2001 at 04:18 UTC
    Thanks everyone. I am now in the right direction.