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

Hi all, I have a problem - I have a script which I need to split the following file into 3 sections:
#!/bin/sh . /etc/rc.common # __START_CONFIG__ binary_app=/usr/bin/true config_file=/etc/inetd.conf MOD_NAME="ABC" # __END_CONFIG__ # this file does nothing.
There are 3 things I need to do here: 1 - I need to grab the all parts up to & including # __START_CONFIG__ & store them in a temp file 123456.beg 2 - I need to grab the next section up to the line with # __END_CONFIG__ & put it into an array called allow_edit 3 - I need to grab the end of the file & store it into a temp file 123456.end Here's what I have so far:
#!/usr/bin/perl # Open File - Read File Contents Then Modify & save contents # Specify name of file $data_file="sample.pm"; # Name of temp file 1 $prefile1="/tmp/123456.beg" # Name of temp file 2 $prefile2="/tmp/123456.end" $action=1; # Open File abd read it all in to rawdata open (outfile1, ">$prefile1") || die ("Could not open file. <br> $!"); +# Open The File open (outfile2, ">$prefile2") || die ("Could not open file. <br> $!"); +# Open The File open (sample, "$data_file") || die ("Could not open file. <br> $!");# +Open The File flock(sample, 2) or die "cannot lock file exclusively: $!";# Lock The +File @rawdata = <sample>;# Put data from file into array called sample # write data from sample.pm into beg_non_edit foreach $value (@rawdata) { print ("$value\n"); if($string =~ /# __START_CONFIG__/i) { $action=2; } if($string =~ /# __END_CONFIG__/i) { $action=3; } if ( $action == 1 ) { # write to outfile1 print outfile1 "$value"; } if ($action == 3) { # write to outfile2 print outfile2 "$value"; } if ($action == 2) { # copy string to new array } } close (sample); close (outfile1); close (outfile2);
My problem is that I can't seem to write into the new files the information - they appear blank - obviously my code is wrong. Secondly, If I then try to rerun the script, it tells me that I cannot open the files. Any help would be really appreciated - thanks, Terry

Replies are listed 'Best First'.
Re: Splitting up file
by linux454 (Pilgrim) on Mar 19, 2006 at 20:48 UTC
    First things first. You need:
    use strict; use warnings;

    That will never fail you.

    To address your problem it looks like you are comparing against the wrong variable in your if statements:

    if($string =~ /# __START_CONFIG__/i) { $action=2; }

    Should be:

    if($value =~ /#\s__START_CONFIG__/i) { $action = 2; }
    (I substituted your space for the \s pattern instead, I like it better though if you strictly need a space, I prefer [ ] instead, that's a matter of style, I just like to be very explicit, and it tells me later that I meant to put a space in the pattern. See if these things help.
Re: Splitting up file
by matze (Friar) on Mar 19, 2006 at 20:55 UTC
    In your loop on @rawdata you match on $string, which is undefined. If you replace it with $value, your code works for me.

    It might be worth considering to use use strict; and use warnings;, because these point out some errors.

    I would also replace the first if with
    if($value =~ /# __START_CONFIG__/i) { $action=2; print outfile1 $value; next; }
    to ensure that this line ends up in the first file. I didn't encounter problems re-running the script, so I can't help you on that one.
Re: Splitting up file
by graff (Chancellor) on Mar 19, 2006 at 22:29 UTC
    I think the other replies have nailed the immediate problem, but here are some extra thoughts...

    1. You don't seem to be replacing the original file with any sort of modified output, so there doesn't seem to be a need to use flock on the input file. (Did you want to replace the input file, too? There's an easy way to do that without needing flock.)

    2. Basic loop over the input file data can be a lot simpler, and since you're reading the whole file into memory anyway, you might as well just hold the initial and final parts in memory until you're ready to write the edited version of the input -- I don't quite see why you need the two extra temp files.

    How about something like this:

    #!/usr/bin/perl my $datafile = "sample.pm"; open( I, $datafile ) or die "open failed on $datafile: $!"; my @editable; my ( $bgn, $end ) = ( '', '' ); my $save = \$bgn; while (<I>) { if ( defined( $save )) { $$save .= $_; $save = undef if ( /^\#\s+__START_CONFIG__/ ); } elsif ( /^\#\s+__END_CONFIG__/ ) { $end .= $_; $save = \$end; } else { push @editable, $_; } } # $bgn holds all input up to and including "# __START_CONFIG__" # $end holds "# __END_CONFIG__ and all input that follows it # @editable holds all lines between $bgn and $end
    You can save $bgn and $end to temp files if you feel the need, but I'll bet you can just hold onto those two scalars until you need to write the output file.

    (And if you're going to replace the original input and want to avoid any risk that someone might read a partially-written version, just save the output to some other file name, and when that file is safely closed, just  rename $newdatafile, $datafile;)

Re: Splitting up file
by hmag (Acolyte) on Mar 20, 2006 at 05:15 UTC
    Hi guys, Many thanks for the replies - I'll see what I can now turn up with your suggestions. The original file will indeed be replaced, but not at this stage - I need to get this part of it sorted out before I do anything else with the resulting files/code.
Re: Splitting up file
by Anonymous Monk on Mar 20, 2006 at 05:03 UTC
    This is fairly easy to do using Perl's flip-flop operator:
    #!/usr/bin/perl # Open File - Read File Contents Then Modify & save contents use warnings; use strict; # Specify name of file my $data_file = 'sample.pm'; # Name of temp file 1 my $prefile1 = '/tmp/123456.beg'; # Name of temp file 2 my $prefile2 = '/tmp/123456.end'; # Open File and read it all in to rawdata open OUTFILE1, '>', $prefile1 or die "Could not open $prefile1. <br> $ +!"; open OUTFILE2, '>', $prefile2 or die "Could not open $prefile2. <br> $ +!"; open SAMPLE, '<', $data_file or die "Could not open $data_file. <br> $ +!"; my @rawdata; while ( <SAMPLE> ) { if ( 1 .. /# __START_CONFIG__/i ) { print OUTFILE1; next; } if ( /# __END_CONFIG__/i .. eof ) { print OUTFILE2; next; } push @rawdata, $_; } close SAMPLE; close OUTFILE1; close OUTFILE2;
    By the way, you can't exclusively lock a file opened readonly. And you should import the flock constants from the Fcntl module instead of using numerical literals.
Re: Splitting up file
by hmag (Acolyte) on Mar 20, 2006 at 08:53 UTC
    Hi again - Many thanks for the help there - that now works fine each & every time - the file rewrite problem turned out to be because I was not running as a user with permissions for writing on /tmp

    Modification of the file need not take place at this time, perhaps functioning the read to get the editable data to populate a web template might be a better concept and then on the post (when the data is changed) perform the reads again but substituting the changes when writing the temp file and when it's all written then move it to avoid any reads of the partial file which may be destructive if the file hasn't been completely written out.

    Can anyone suggest how to accomplish this? I'm no programmer by any stretch of the imagination & any help you guys could give me would really be appreciated.