Perl - How to omit lines from a text file?

Viewed 95

I have a text file, and I'm looking to omit some of the lines from the text file, and use that string to create a new file. The nice thing is that my text file starts the text chunk that I need with a line that includes "START" and ends with "END".

For example, my text file looks like:

1
2
3
Start
4
5
6
End
7
8
Start
9
10
End

The desired output would be two strings that I can output into text files that look like:

Start
4
5
6
End
Start
9
10
End

What I currently have so far:

open(RH, '<', $fileName) or die $!;

while(<RH>) {
    #print $_;
    chomp $_;
    if ($_ eq 'START') {
        $str = "$str"."$_\n";
    }
}

But I'm not sure how to continue.

EDIT: I answered this question using:

$cmd = q(awk '/Start/,/End/ {print}' foo.txt);
my $output = qx($cmd);
my @cards = split (/(?<=\End)/, $output);
4 Answers

You can use some of Perl's heritage from AWK and just do this (assuming your file is called foo.txt)

perl -ne'print if /Start/../End/' foo.txt

The expression /Start/../End/ means "at the first line that matches /Start/ up to the next line that matches /End/.

The equivalent code for awk would be

awk '/Start/,/End/ {print}' foo.txt
# Read the entire file into a string `$str`:
open my $fh, '<', 'file_name' or die "Can't open file $!";
my $str = do { local $/; <$fh> };
close $fh;

while ($str =~ m{\n(START\n.*\nEND)\n}msg) {
    # Do something with each START...END set of lines
    print "$str\n";
}

Notes:

  • I'm not sure of all the details.
  • The local $/; might be done by something like undef $/;
  • Adjust the parens to avoid capturing 'START' and 'END'.

Use GNU grep:

grep -Poz '(?ms)^Start.*?^End\n' in_file

Here, GNU grep uses the following options:
-P : Use Perl regexes.
-o : Print the matches only (1 match per line), not the entire lines.
-z : Treat input and output data as sequences of lines, each terminated by a zero byte (the ASCII NUL character) instead of a newline. Thus, you can match newlines in the input.

(?ms) : Enable the m and s pattern-match modifiers, to allow multiline matches, and to allow . to match a newline, respectively.

SEE ALSO:
grep manual
perlre - Perl regular expressions

Use .. as a "flip-flop" operator.

# Switch to a lexical filehandle
# (as this is modern best practice)
open(my $rh, '<', $fileName) or die $!;

# Open an output filehandle
my $x = 1;
open my $out, '>', "$filename.out.$x" or die $!;

while(<$rh>) {
  print $out $_ if /Start/ .. /End/;

  # Open a new output file if we've seen 'End'
  if (/End/) {
    ++$x;
    open my $out, '>', "$filename.out.$x" or die $!;
  }
}
Related