if match found go to a particular line in perl

Hello Experts,

   I am newbie to perl, just curious to know how to do the following in perl.

suppose I ve a txt file like below. when it founds "*Main Start"
Then go to "*Main End,,,,,,,," patteren and just collect the number from the previous line of "*Main End,,,,,,," pattern . In my case it is 5 . Then start excuting normally from the "*Main Start" position

Following is the picture .. I dont want to create any temp file also..
Input file:

*Init End
*Main Start
*Comment Reset Timers
000000,0000,0,0,0,0,0,1,0
000000,0000,0,0,0,0,1,1,0 
*Comment Control Frame at 1.04596 ms
000000,0400,0,0,0,0,1,0,1
2418A4,0000,0,1,3,0,0,0,0 
049C00,0000,0,0,2,0,0,0,0
*Comment Control Frame at 1.04673 ms
*Comment Control Frame at
000002,0000,0,0,0,0,1,0,1
241002,0000,0,1,3,0,0,0,0
000100,0000,0,0,2,0,0,0,0
*Comment Control Frame at
000004,0000,0,0,0,0,1,0,1
241002,0000,0,1,3,0,0,0,0
000000,0000,0,0,2,0,0,0,0
*Comment Frame 13 at ** us,,,,,,,,
000005,7E3D,0,0,0,0,1,0,1
*Main End,,,,,,,,

output:

*Init End
*Main Start
 repeat(5);
process all lines as usual here.

............
*Main End,,,,,,,,,

I ve to print all lines as it is when it found a line starting with * . Thats why I am doing like below

 if(m/^\*/){
              print "//", $_;
              next;
    }
Basic Algorithm of my programme need :
1. If "*Main Start" pattern found go to line where Pattern matches "*Main end".
2. Get the previous line. and get the value of the first field.
3. Return to "*Main start" position again. & Print repeat(that number) : in my case it is 5 :
4. stop/next;

Any help most appriciated..

regards,
user_prady

Hi friends is there anyway to do it. Pls help ..

Thanks

Don't depend on me, I need to be going back to my day job soon.

There's a number of ways to do this, obviously. The old-fashioned variant would be to remember the previous line and print that when you see the terminator. The really brute Perl approach would be to slurp the whole file and substitute everything with an empty string except the line before the terminator. There's one in the Perl FAQ about that; perlfaq6 and scroll around for related questions. (The question about C comments further down the page has some hints, too.)

But the "previous line" solution is absolutely the simplest in this case, if you have no further requirements.

perl -ne 'BEGIN { $matching = 0; }
  $matching = 1 if (m/^\*Main Start/);
  next unless $matching;
  print $prev if (defined $prev && m/^\*Main End/);
  $prev = $_'

Not sure about the flow control you tried to describe. Are you supposed to remember everything from main start until main end and print that after the output from the last line before main end? (I guess this already qualifies as a pseudo-code implementation of what it takes. But then maybe I would consider a regex substitution over the whole file, or each *Main Start section, after all.)

Thanks a Lot Era for your kindness..I ll try your mentioned way..

I don't think I have captured all your requirements correctly but at least it's a start. If I am allowed to guess, what would be easiest is collect the output in a variable and print it after you have seen *Main End and printed out the line which immediately preceded it.

I tried almost all way using next , redo and last command in perl , But I can't get these syntax correctly in perl. I know its easy ,but still I cant find the efficient way using while and next statement in perl.

I am trying to do the follwoing in perl

nawk ' {
        if($0 ~ /^\*Main Start/){
	     while( $0 !~ /^\*Main End/){
	     print $0;
             getline;
        }
     }
 } ' my.txt

One more code I want to do it in perl pls give me some basic idea how to do it without creating any temporary files ..

TMP=/tmp/my_tmp$$
nawk '
/\*Main End/{
split(x,arr,",")
print "Loop  " arr[1]
};
{x=$0
}
' my.txt > $TMP 

nawk '{
if($0 ~ /^Loop/){
    loop = $2 
    next;
  }
if($0 ~ /^\*Main Start/){
     printf "\nrepeat ("
     print  loop

} ' $TMP $my.txt 

Thanks & Regards,
user_prady

You could use the Tie::File module for this which avoids the brute approach "era" mentioned. With Tie::File you treat a file like a perl array, so if you find a pattern in a certain line you just use simple array indexing to go back one line in the file. See the Tie::File (a perl core module) documentation for usage details.

At last I got one way to do it.

my @buffer;
$#buffer = 1;
open(IN, $file)|| die("Could not open file");
 
while(<IN>){
        push@buffer => $_;
        shift@buffer;
}
close(IN);
open(IN, $file)|| die("Could not open file");
while(<IN>){ 
       print $buffer[0] if/^\*Main Start/ ;
}
close(IN);

Regards,
prady

Thanks Kevin for your time and kind ..

But when I am trying to use the module Tie::File I am getting the follwing error.

Can't locate Tie/File.pm in @INC (@INC contains: /usr/perl5/5.00503/sun4-solaris /usr/perl5/5.00503 /usr/perl5/site_perl/5.005/sun4-solaris /usr/perl5/site_perl/5.005 .) at ./mdl.pl line 5.
BEGIN failed--compilation aborted at ./mdl.pl line 5.

This is first time I'll use a module so pls if I am doing something wrong with my code pls suggest me.

use Tie::File;
tie @array, 'Tie::File', $file or die ("Could not open file");
print $array[13] ;

Regards,
user_prady

Tie::File has been a core module for a while, but I am not sure since when. The problem is that your server is using a very old version of perl 5.005, so either it was not included with the distribution of perl you have or it was removed for some reason.

You could do something like this:

use strict;
use warnings;
my $flag = 0;
my $last_line;
while(<DATA>){
   if(/^\*Main Start/){
      $flag = 1;
   }
   elsif($flag && /^\*Main End/) {
         $numbers = (split(',',$last_line))[0];
         $last_line = '';
         $flag = 0; 
         print $numbers,"\n";
   }
   else {
      $last_line = $_;
      next;
   }
}	
__DATA__
*Init End
*Main Start
*Comment Reset Timers
000000,0000,0,0,0,0,0,1,0
000000,0000,0,0,0,0,1,1,0 
*Comment Control Frame at 1.04596 ms
000000,0400,0,0,0,0,1,0,1
2418A4,0000,0,1,3,0,0,0,0 
049C00,0000,0,0,2,0,0,0,0
*Comment Control Frame at 1.04673 ms
*Comment Control Frame at
000002,0000,0,0,0,0,1,0,1
241002,0000,0,1,3,0,0,0,0
000100,0000,0,0,2,0,0,0,0
*Comment Control Frame at
000004,0000,0,0,0,0,1,0,1
241002,0000,0,1,3,0,0,0,0
000000,0000,0,0,2,0,0,0,0
*Comment Frame 13 at ** us,,,,,,,,
000005,7E3D,0,0,0,0,1,0,1
*Main End,,,,,,,,

Thanks Kevin.

I read your previous article how to fix the problem on the module issue

Perl - read a line from a file and writing to the same line in the file

But still getting the error....

Anyway many many thanks for your time and given code. I'll try it.

Regards,
prady

Yes Kevin . I am pretty close to my goal but one thing that I want the number (ie, 000005) when *Main Start pattern found .

Regards,
user_prady

With the sample data you posted, the code I posted does that, it prints 000005.

Thanks a lot again..
Yeah you are right It prints , but my problem here is when the pattern *Main start found I need that no.
But in the above code I got the number before *Main End Pattern.

Regards..

What? There is no number before "*Main Start". You asked how to get the number one line before "*Main End". Or maybe I just do understand your question.

I think user_prady means the information needs to be printed already at the point when you see the Main Start although it occurs later in the file. So you need to either move things around in the file (the ugly multi-line regex substitution solution) or keep the stuff between Main Start and Main End in memory, and then print the number first, and then all the lines you have been keeping in memory. However, I'm afraid I don't know how to explain how to do this in more detail without repeating myself. In pseudo-code, something like

if main_start seen then
  do whatever you do but don't print yet
  instead, push @memory, whatever you would have printed
if main_end then
  print the last line from @memory (pop @memory)
  then print all the other stuff from @memory

In real-world code, you have to move things around a little (figure out if you are coming out of "main start seen" as basically the first thing), but this is only pseudo-code, mind.

I'm reluctant to write code before we get confirmation that this is the actual requirement, though.

After re-reading his first post I think you are correct era.

This should be pretty easy with Tie::File or read the entire file into an array, edit the array and then overwrite the file with the new array. I think you may have already suggested that approach.