Search code examples
loopsperlfile-io

Print lines followed by a line matching a pattern, to file A, if no match then print to file B


I am trying to write a Perl script to parse a file and segregate its contents based on a match criteria. A sample file (for e.g. test.txt) would look like something:

command something something keyword something something
filepath1
filepath2
command something something something something
filepath3
filepath4
filepath5

The output of the script would be two files based on the name of input file for the script, test.keyword and test.nomatch.

test.keyword would have something like this:

command something something keyword something something
filepath1
filepath2

test.nomatch would have something like this:

command something something something something
filepath3
filepath4
filepath5

The following is the skeleton that I have at the moment which is waiting for that golden loop condition:

#!/usr/bin/perl -a

my $fh = shift or die "No file passed as argument for splitting!\n";
open( infile, "<$fh" ) or die $!;
open( vef_output, ">$fh.vef" ) or die $!;
open( svf_output, ">$fh.svf" ) or die $!;

$mode = 0; #0 for vef and 1 for svf

while ( my $line = <$infile> ) {
        if ( $line =~ /SOME_LIBRARY_OPTIONS/ ) {
        if ( $line =~ /\-sv/ ) {
            print {$svf_output} $line;
            $mode = 1;
        }
        else {
            print {$vef_output} $line;
            $mode = 0;
        }
        next;
    }
    if ( $mode eq 0 ) {
        print {$vef_output} $line;
        next;
    }
    else {
        print {$svf_output} $line;
        next;
    }   
}
close($vef_output);
close($svf_output);
close($file);

Solution

  • While the logic of your code is correct and you surely will find some remaining typos on your own, I would like to suggest a modification to your while loop:
    Each line of your input file will have to be printed once (except perhaps for the beginning of the input file). Instead of setting a $mode flag and testing that, I'd rather switch the output filehandle , which leads to clearer code:

    #!/usr/bin/perl
    use strict;
    use warnings;
    
    my $filename = shift or die "No file passed as argument for splitting!\n";
    
    # add filename to error message - users will like it!
    open( my $infile, "<", $filename ) or die "could not open $filename: $!";
    open( my $vef_output, ">","$filename.vef" )
        or die "could not open $filename.vef: $!";
    open( my $svf_output, ">","$filename.svf" )
        or die "could not open $filename.svf: $!";
    
    my $active_out;
    
    while ( my $line = <$infile> ) {
        if ( $line =~ /SOME_LIBRARY_OPTIONS/ ) {
            $active_out = $vef_output;
        }
        # depending on your regex this conditional can be nested or not...
        if ( $line =~ /-sv/ ) {
            $active_out =  $svf_output;
        }
        next unless defined $active_out;
        print $active_out $line;
    }
    
    close($vef_output);
    close($svf_output);
    close($infile);