For Programmers: Free Programming Magazines  


Home > Archive > PERL Beginners > November 2005 > Matching multiple lines in a regex.









You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

 

Author Matching multiple lines in a regex.
s4mmy

2005-11-18, 3:55 am

Hello all,

I've looked over the perlfaq and some other documents regarding
matching multiple-line strings with a regex, and accomplished that
(partly). I run into a problem when I am trying to pick out a certain
entry though.
Here's what I'm trying to do:
I need to remove the zone entry in a named.conf file with multiple
other entries. The entry/entries look like:

zone "foobar.com" {
type master;
file "addr/foobar.com";
};

What I have done so far is been able to match it using '/^zone.*;$/s'
for a file with one record only (to see if the regex matched.) However,
when I use it like this:

while(<NFILE> ) {
s/^zone.*;$//s;
}
....it doesn't match. Could someone please show me what I am doing
wrong? I feel it's some sort of boneheaded error that I should know but
am simply overlooking, or perhaps it is a nuance of Perl I'm not
familiar with (I'm relatively new to it.)

Thank you in advance,

Sam
NB. This is my first post here, so I hope I posted appropriately.

Eden Cardim

2005-11-18, 7:55 am

> Here's what I'm trying to do:
> I need to remove the zone entry in a named.conf file with multiple
> other entries. The entry/entries look like:
>
> zone "foobar.com" {
> type master;
> file "addr/foobar.com";
> };
>
> What I have done so far is been able to match it using '/^zone.*;$/s'
> for a file with one record only (to see if the regex matched.)


This works, but probably isn't doing what you expect. Since pattern
matches are greedy by default, the .*; part of your regexp will gobble
up everything in your data up to the last ; character. A better regexp
would be:

/^zone.*?};$/

> However,
> when I use it like this:
>
> while(<NFILE> ) {
> s/^zone.*;$//s;
> }
> ...it doesn't match. Could someone please show me what I am doing
> wrong? I feel it's some sort of boneheaded error that I should know but
> am simply overlooking, or perhaps it is a nuance of Perl I'm not
> familiar with (I'm relatively new to it.)


It won't match because the default record separator is "\n", that would
make the first call to <NFILE> read 'zone "foobar.com" {' which won't
match because the last character isn't a ;
You could slurp in the entire file with:

my $file;
{ my $/;
$file = <NFILE>;
}

and then do the replacement with:

$file =~ s/^zone.*?};$//s;

xicheng

2005-11-18, 6:57 pm

if you don't have any other lines containing only "};" in the "zone"
entries, you may try it this way:

while (<NFILE> ) {
print unless /^zone/../^}\;$/;
}

s4mmy

2005-11-18, 6:57 pm


Eden Cardim wrote:
[ snip ]

> It won't match because the default record separator is "\n", that would
> make the first call to <NFILE> read 'zone "foobar.com" {' which won't
> match because the last character isn't a ;
> You could slurp in the entire file with:
>
> my $file;
> { my $/;
> $file = <NFILE>;
> }
>
> and then do the replacement with:
>
> $file =~ s/^zone.*?};$//s;


First of all, thanks for your reply. This is what I came up with,
however, now I am just trying to substitute out in the file. Got
something like this:

open (NFILE,'+<',"/etc/named/named.conf");
{
local $/ = undef;
my $file = <NFILE>;
$file =~ s|/^zone "$domain"/ .. /\};$/||
print NFILE $file;
}
But it is not interpolating the value from the range-operator regex.
Kind of at a loss here! This is what I want it to do:
1. Find the multiline record given the domain $domain.
2. Substitute that multiline record and print back to the file.

I'm sure there is an easy way to do this, but googling seems to turn up
a lot of things about simply using perl -i -pe from the command line.

TIA,
Sam

Eden Cardim

2005-11-18, 6:57 pm

--snip--
> open (NFILE,'+<',"/etc/named/named.conf");
> {
> local $/ = undef;
> my $file = <NFILE>;
> $file =~ s|/^zone "$domain"/ .. /\};$/||
> print NFILE $file;
> }
> But it is not interpolating the value from the range-operator regex.
> Kind of at a loss here! This is what I want it to do:
> 1. Find the multiline record given the domain $domain.
> 2. Substitute that multiline record and print back to the file.


$file =~ s|/^zone "$domain"/ .. /\};$/||
this looks weird... why use pipes as delimiters and keep the slashes
inside? I don't see any range operators either... ? Could you explain
what this regexp is supposed to do?
Also, you should back up your original file and then use open with +>
to clobber your old file with the modified version.
The rest of the code looks fine...

s4mmy

2005-11-18, 6:57 pm

[snip]
> $file =~ s|/^zone "$domain"/ .. /\};$/||
> this looks weird... why use pipes as delimiters and keep the slashes
> inside? I don't see any range operators either... ? Could you explain
> what this regexp is supposed to do?
> Also, you should back up your original file and then use open with +>
> to clobber your old file with the modified version.
> The rest of the code looks fine...


I found the "solution" here:
http://www.unix.org.ua/orelly/perl/cookbook/ch06_09.htm

this regex --> /^zone "$domain"/ .. /\};$/ will match the entire zone
record, across newlines, and terminate when it reaches the }; on last
line. Note the range operator in between the two regex's.

What I'm basically trying to do is put one regex inside another, as it
were (hence the pipes and slashes):
s||| becomes s|/^zone "$domain"/ .. /\};$/||; -- I want it to
substitute the zone record for $domain with nothing, but it is not
interpolating the regex. I also tried $rgx = qr/regex_here/ and
s/$rgx//; to no avail.

I might be way off the beaten path, but I'm trying =) And thanks for
the open() tip.
TIA,
Sam

Eden Cardim

2005-11-18, 6:57 pm

> this regex --> /^zone "$domain"/ .. /\};$/ will match the entire zone
> record, across newlines, and terminate when it reaches the }; on last
> line. Note the range operator in between the two regex's.
> What I'm basically trying to do is put one regex inside another, as it
> were (hence the pipes and slashes):
> s||| becomes s|/^zone "$domain"/ .. /\};$/||; -- I want it to
> substitute the zone record for $domain with nothing, but it is not
> interpolating the regex. I also tried $rgx = qr/regex_here/ and
> s/$rgx//; to no avail.
> I might be way off the beaten path, but I'm trying


You misunderstood the recipe, what you saw were two regexps with a
range operator between them being called in boolean context. Besides,
regexps don't interpolate inside each other, that wouldn't make any
sense. Perl operators aren't usable inside regexps either. Just use the
substitution operator and you'll be fine:

$file =~ /^zone.+?};$//;

xicheng

2005-11-18, 6:57 pm

The problem for him is that he can NOT undefine $/ when using /A/ ..
/B/

his problem should be a very classic problem to be solved by combing
/A/ .. /B/ with "if" or "unless" block, and can be done by Perl
one-liner, like:

perl -i.bak -ne 'print unless /A/ .. /B/' named.conf

In his case /A/ is /^zone/ , and /B/ is /^}\;$/
A and B are independent regex which express the features of the
starting line and ending line of the block and can distinguish them
from other lines in his document.

Sponsored Links







Also available: Server administration forum archive | Web Design forum archive | Software forum archive | Hardware reviews archive

Copyright 2008 codecomments.com