0

I have several files (*.txt) that I need to remove lines from. The files look like this:

This is a line to keep.
keep me too
START
some stuff to remove
other to remove
END
keep me!

And I want them to look like this:

This is a line to keep.
keep me too
keep me!

I've gotten this far:

perl -i -p0e 's/#START.*?END/ /s' file.txt

Which will remove the first instance of that from file.txt, but I can't figure out how to remove all instances from file.txt (and then how to apply this to all *.txt files?)

2
  • This is simple, but does it have to be a one-liner? Are the files supposed to be changed in place, or new ones written, or do you want just one big file as output? Commented Jun 30, 2016 at 19:24
  • Thanks @zdim No, it doesn't have to be a one-liner. I don't care whether they're changed in place or not, and I want individual files as output. Commented Jun 30, 2016 at 19:25

3 Answers 3

5

If what you show works for the first instance, all you should need to add is the /g flag to do all instances, and a shell glob to pick out all .txt files:

perl -i -p0e 's/#START.*?END/ /gs' *.txt
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks. I had tried adding the /g flag before but it just deleted everything in all the files. This works, though, so I must've done something wrong before. Thanks a ton!
3

This seems to be right for the flip-flop operator

#!/usr/bin/env perl

use strict;
use warnings;

while( <DATA> ) {
    print unless (/^START/ .. /^END/);
}

__DATA__
This is a line to keep.
keep me too
START
some stuff to remove
other to remove
END
keep me!

Output:

This is a line to keep.
keep me too
keep me!

It can also be written as a one-liner:

perl -n -e 'print unless (/^START/ .. /^END/);' input.txt > output.txt

Or, to edit the files in-place:

perl -n -i -e 'print unless (/^START/ .. /^END/);' *.txt

1 Comment

@CasimiretHippolyte You're right. Thank you. Updated my answer.
1

A bookkeeping thing to take care of here is opening and writing of individual files. The processing itself is handled by the range operator.

use warnings;
use strict;

my @files = @ARGV;

my ($fh_in, $fh_out);

foreach my $file (@files) 
{
    my $outfile = "new_$file";

    open $fh_in, '<', $file  or die "Can't open $file: $!";
    open $fh_out, '>', $outfile  or die "Can't open $outfile: $!";

    print "Processing $file, writing to $outfile.\n";

    while (<$fh_in>) {
        print $fh_out $_ if not /^START$/ .. /^END$/;
    }
}

This is invoked as script.pl file-list.

Since we use the same filehandle for reading (and the same one for writing), when a new file is opened the previous one is closed, see perlopentut and open. So we don't have to close

You don't have to close FILEHANDLE if you are immediately going to do another open on it, because open closes it for you. (See open.)

I name the new files as new_$file, just to provide a working sample. You could, for example, rename the old one to $file.orig and new one to $file instead, after the while loop. I'd use functions from the core File::Copy module for this. In this case we do need to close files explicitly first.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.