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

Hi,

I'm writing a script to upgrade an apache configuration file and have a script which reads through each line using the while(<>) contruct.

My question is, what's the best way to match on a block of text such as below:

Alias /icons/ "/opt/apache_2.0.48/icons/" <Directory "/opt/apache_2.0.48/icons/"> Options Indexes MultiViews AllowOverride None Order allow,deny Allow from all </Directory>

and change this to:

#Alias /icons/ "/opt/apache_2.0.54/icons/" #<Directory "/opt/apache_2.0.54/icons"> # Options Indexes MultiViews # AllowOverride None # Order allow,deny # Allow from all #</Directory>

Thanks for any help,

js1.

Replies are listed 'Best First'.
Re: match and edit block of text
by mugwumpjism (Hermit) on Jun 03, 2005 at 06:22 UTC

    Once you've decided what it is you need to comment out, then simply use;

    s/^/#/

    So, you could use something like;

    while (<>) { if (/Alias/ .. /\/Directory/) { s/^/#/; } print OUTPUT; }

    That is, if your intent is to comment out all lines starting from the one matching /Alias/ to the one matching /\/Directory/.

    See the perlfunc man page, under s/// for how to use s///, the perlretut and/or perlrequick man page for how to write regular expressions, and the perlop man page for how the .. "flip-flop" operator works.

    $h=$ENV{HOME};my@q=split/\n\n/,`cat $h/.quotes`;$s="$h/." ."signature";$t=`cat $s`;print$t,"\n",$q[rand($#q)],"\n";

      Thanks for your reply. This did exactly what I wanted:

      # comment out icons section if (/Alias \/icons/ .. /\/Directory/) { ($newline=$_)=~s/^/#/; }
Re: match and edit block of text
by bart (Canon) on Jun 03, 2005 at 06:41 UTC
    You don't say how you want to match it. Anyhow, this is the basics of the replacement:
    • use the -i flag, or set $^I to a defined value
    • put the path of the httpd.conf file in @ARGV
    • loop through <>
    • edit the lines you want to change
    • and print
    Code:
    { local $^I = ".bak"; local *ARGV; @ARGV = "/path/to/httpd.conf"; while(<>) { s/^(?=.)/# /; # simple substitution print; } }
    You'll probably have to make the rules for the update more complex. Here's one way to match a <Directory> block. Put the following code block inside the while(<>){ ... } block:
    if((my($dir) = /^<Directory\s+(.*)>/i) ... /<\/Directory>/i) { if(defined $dir) { # first line in the block for ($dir) { s/^"//; s/"$//; # remove quotes $icons = m(/icons$); } } if($icons) { ... # do something with these lines } }

    $icons may not be declared inside the loop, or it'll be undef next time you go through the loop.

Re: match and edit block of text
by rg0now (Chaplain) on Jun 03, 2005 at 07:14 UTC
    By the way: why not using prefab code, like Apache::Admin::Config? Apache::ConfigFile is worth a look, too...

    Update: here is the code. Instead of commenting out the offending section you can just simply remove it like this:

    use strict; use warnings; # remove the <Directory "/opt/apache_2.0.48/icons/"> section use Apache::Admin::Config; my $conf = new Apache::Admin::Config "your_apache_conf.conf" or die $Apache::Admin::Config::ERROR; my $section = $conf->select('section', 'directory', '"/opt/apache_2.0. +48/icons/"'); $section->unlink; print $conf->dump_raw;
Re: match and edit block of text
by aukjan (Friar) on Jun 03, 2005 at 06:26 UTC
    Please expand on how you want to match a block. If you want to comment out the Alias and the belongning 'Directory' block, you should match the path like:
    m/Alias.+"(.+)"/;
    And then use $1 to match:
    m/<Directory\s+"$1">/;
    Then append a '#' to that line until you reach the end of the Directory block.

    .:| If it can't be fixed .. Don't break it |:.

Re: match and edit block of text
by Zaxo (Archbishop) on Jun 03, 2005 at 06:20 UTC
    while (<>) { print '#' if $_ ne $/; print; }

    You may have some other conditions to apply, but you didn't mention them.

    After Compline,
    Zaxo

Re: match and edit block of text
by ww (Archbishop) on Jun 05, 2005 at 15:07 UTC
    js1

    Is there any importance to you that the version numbers (2.0.48 vs 2.0.54) change in your example?

    If not, you have splendid answers already. If there is something important to you (clearly, not to the functionality), then you may wish to do, as a second pass, another pass with a match_and_capture on "2.0.48" AFTER a "#", and a substitution or assignment, to effect the second change.