I feel like I am missing something very simple here, but this is the first time I've needed to do this and am having trouble finding an example.
I have a giant foreach
loop that walks through output logs and extracts various bits of information based on matching regular expressions. My main problem is that a few larger types of output have a header and footer, like *** Begin bangle tracking log***
followed by several lines of gibberish and then a ***End bangle tracking log***
.
Is there a way, from within a foreach
loop, to have an inner loop that stores all the lines until a footer is found?
foreach my $line( @parseme )
{
if( $line =~ m/***Begin bangle tracking log***/ )
{
#Help! Push all lines into an array until bangle tracking footer is found.
}
if( $line =~ m/Other stuff I am tracking/ )
{
#Do other things
}
}
You could use the range operator, which acts as a flip-flop in scalar context:
foreach ( @parseme ) {
if ( /Begin bangle tracking log/ .. /End bangle tracking log/ ) {
push @array, $_;
}
# other stuff...
}
I used $_
for the foreach
loop because it allows for more concise syntax. You can use another variable if you like, but then you'll have to write the condition as something like:
if ( $line =~ /Begin .../ .. $line =~ /End .../ ) {
which might be more readable with some extra parentheses:
if ( ($line =~ /Begin .../) .. ($line =~ /End .../) ) {
One issue to note about the flip-flop operator is that it remembers its state even after the loop ends. This means that, if you intend to run the loop again, you really ought to make sure that the @parseme
array ends with a line that matches the /End .../
regexp, so that the flip-flop will be in a known state when the loop starts the next time.
Edit: Per DVK's comment below, if you want to process the collected lines as soon as you reach the footer line, you can do that by checking the return value of the ..
operator, which will end with E0
on the last line:
foreach ( @parseme ) {
my $in_block = /Begin bangle tracking log/ .. /End bangle tracking log/;
if ( $in_block ) {
push @array, $_;
}
if ( $in_block =~ /E0$/ ) { # last line
# process the lines in @array
@array = ();
}
# other stuff...
}
You can do that somewhat easily by implementing a primitive state machine:
my $inside_bangle = 0; # 0=outside block, 1=inside
my @buffer;
foreach my $line( @parseme ) {
if ($line =~ m/***Begin bangle tracking log***/ ) {
$inside_bangle = 1;
next;
}
if ($line =~ m/***End bangle tracking log***/ ) {
$inside_bangle = 0;
# PROCESS @buffer somehow
next;
}
if ($inside_bangle) {
push @buffer, $line;
next;
}
if ($line =~ m/other stuff i am tracking/ ) {
#Do other things
}
}
Another option is to use flip-flop (..
)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With