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

I have the following text in a file -
<IfModule mod_limitipconn.c> <Location /> MaxConnPerIP 12 NoIPLimit image/* </Location> <Location /location2> MaxConnPerIP 0 </Location> <Location /location3> MaxConnPerIP 0 </Location> </IfModule>
I am trying to open up the file, slurp the date, go into the Location block and based on a check on the location I would like to change the value of MaxConnPerIP. What I have so far is
#!/usr/bin/perl use strict; use warnings; use Tie::File; use File::Copy; my $f = 'ipconn.conf'; my $location = '/'; # Was an attempt to parameterize the location fi +eld that way I can toggle through it once I have the below loop worki +ng. tie my @filelines, 'Tie::File', "$f", autochomp => 0 or die $!; foreach ( @filelines ) { if (!/^#/ && m/\<Location\s*(.+?)\<\/Location\>/ && /^\s*Ma +xConnPerIP\s*/) { $_ = '#' . localtime . $_; $_ .= "\n\t\tMaxConnPerIP 10"; } else { print "in else"; } } untie @filelines or die $!;
I understand I have the regex wrong or also I might need to loop through the block to find the tesxt i wish to change an update it.That was based on the idea - http://www.perlmonks.org/?node_id=153498 So if I can get into the Location block store everything into a string and then search/replace the attribute value, then I could loop through this for each location i wish to edit. If I can search for a block of text using a regex then can I also not to a search-replace in the same regex itself. Also I not sure why but the match operator does work if I open the file using
open (FILE, "< ipconn.conf") or die $!; my $file = <FILE>; close FILE; if ($file =~ m/\<Location\ \/.+?\<\/Location\>/s) print $&;
Then I am able to get into the block. Would like to know what you guys think.

Replies are listed 'Best First'.
Re: Search replace with a block
by hippo (Archbishop) on May 11, 2016 at 08:53 UTC
    Also I not sure why but the match operator does work if I [slurp the whole file into a scalar]

    Your example with the tied file loops over each line at a time. One line will never match both the start and the end of a Location block because they are on different lines. Conversely, when you slurp the whole file into a scalar, then it can match.

      I understand the difference now. Thank you. Is it more simple if I slurp the entire thing in and match the start end and replace the value as compared to doing a tie::file ? Also I am sorry for the noobish question, but is there a way to get into the match's block and do the replace in a single regex.

        There are many, many different ways to approach this and identifying which is simplest is a very subjective measure. Try some of the approaches and see which you think appears simplest to you and just go with that one. TIMTOWTDI.

        You could construct a single regex to do a multiline match and replace but that will certainly be far from the simplest approach. I would recommend Apache::Admin::Config over such a complicated approach any day. Note that this is a pure-perl module so you can just use the source if necessary.

        However, here's the general all-in-one method:

        #!/usr/bin/env perl use strict; use warnings; my $in = <<EOT; <Location /> MaxConnPerIP 12 NoIPLimit image/* </Location> EOT my $max = 16; my $out = $in; $out =~ s/(<Location .+?MaxConnPerIP\s+)(?:\d+)(.+?<\/Location>)/$1$ma +x$2/s; print $out;
Re: Search replace with a block
by beech (Parson) on May 11, 2016 at 07:26 UTC

    Then I am able to get into the block. Would like to know what you guys think.

    Hi

    Have you heard of Config::General or Config::ApacheFormat and Apache::Admin::Config?

    #!/usr/bin/perl -- use strict; use warnings; use Data::Dump qw/ dd /; use Config::General qw(ParseConfig); my $rawconfig = q{ <IfModule> ## workaround of some kind <IfModule mod_limitipconn.c> <Location /> MaxConnPerIP 12 NoIPLimit image/* </Location> <Location /location2> MaxConnPerIP 0 </Location> <Location /location3> MaxConnPerIP 0 </Location> </IfModule> }; my %config = ParseConfig(-String => $rawconfig, -AutoTrue => 1); dd( \%config ); __END__ { IfModule => { IfModule => { "mod_limitipconn.c" => { Location => { "" => {} }, M +axConnPerIP => 12, NoIPLimit => "image/*" }, }, Location => { "/location2" => { MaxConnPerIP => 0 }, "/location3" => { MaxConnPerIP => 0 }, }, }, }

    Some of these modules are worth a try or two :)

      Hi, Yes I did play around wit those. There are two problems - 1. I would have a very hard time getting to install those libraries, read red-tapism. 2. I tested admin::config, admin::config was unable to parse this file as my file is a sub file which is included in the global confi. The library goes looking for some standard stuff which would be present ideally in a httpd.conf file. Config::General I did play around but wasn't to get far.
        ok, maybe something like this, filter every location block
        $rawconfig =~ s{ <location \s+ (\S+?)> (.+?) </location> }{ ReformulateLocation("$1", "$2"); }gimsex; print $rawconfig; sub ReformulateLocation { my( $loc, $con ) = @_; my @pairs = $con =~ m{ ^ \s* (\S+) \s+ ([^\r\n]+?) \s* $}gmix; dd( { $loc, \@pairs } ); return "<location $loc> $con </location>"; ## no change } __END__ { "/" => ["MaxConnPerIP", 12, "NoIPLimit", "image/*"] } { "/location2" => ["MaxConnPerIP", 0] } { "/location3" => ["MaxConnPerIP", 0] } <IfModule> ## workaround of some kind <IfModule mod_limitipconn.c> <location /> MaxConnPerIP 12 NoIPLimit image/* </location> <location /location2> MaxConnPerIP 0 </location> <location /location3> MaxConnPerIP 0 </location> </IfModule>
        Also perlfaq6 has some tips