Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to remove one line from a file using Perl?

Tags:

perl

I'm trying to remove one line from a text file. Instead, what I have wipes out the entire file. Can someone point out the error?

removeReservation("john");

sub removeTime() {
    my $name = shift;

    open( FILE, "<times.txt" );
    @LINES = <FILE>;
    close(FILE);
    open( FILE, ">times.txt" );
    foreach $LINE (@LINES) {
        print NEWLIST $LINE unless ( $LINE =~ m/$name/ );
    }
    close(FILE);
    print("Reservation successfully removed.<br/>");
}

Sample times.txt file:

04/15/2012&08:00:00&bob
04/15/2012&08:00:00&john
like image 208
varatis Avatar asked Apr 15 '12 23:04

varatis


3 Answers

perl -ni -e 'print unless /whatever/' filename
like image 107
Daniel C. Sobral Avatar answered Nov 09 '22 17:11

Daniel C. Sobral


Oalder's answer is correct, but he should have tested whether the open statements succeeded or not. If the file times.txt doesn't exist, your program would continue on its merry way without a word of warning that something terrible has happened.

Same program as oalders' but:

  1. Testing the results of the open.
  2. Using the three part open statement which is more goof proof. If your file name begins with > or |, your program will fail with the old two part syntax.
  3. Not using global file handles -- especially in subroutines. File handles are normally global in scope. Imagine if I had a file handle named FILE in my main program, and I was reading it, I called this subroutine. That would cause problems. Use locally scoped file handle names.
  4. Variable names should be in lowercase. Constants are all uppercase. It's just a standard that developed over time. Not following it can cause confusion.
  5. Since oalders put the program in a subroutine, you should pass the name of your file in the subroutine as well...

Here's the program:

#!/usr/bin/env perl 

use strict; 
use warnings; 

removeTime( "john", "times.txt" ); 

sub removeTime { 
    my $name      = shift;
    my $time_file = shift;

    if (not defined $time_file) {
        #Make sure that the $time_file was passed in too.
        die qq(Name of Time file not passed to subroutine "removeTime"\n);
    }

    # Read file into an array for processing
    open( my $read_fh, "<", $time_file )
       or die qq(Can't open file "$time_file" for reading: $!\n); 

    my @file_lines = <$read_fh>; 
    close( $read_fh ); 

    # Rewrite file with the line removed
    open( my $write_fh, ">", $time_file )
        or die qq(Can't open file "$time_file" for writing: $!\n);

    foreach my $line ( @file_lines ) { 
        print {$write_fh} $line unless ( $line =~ /$name/ ); 
    } 
    close( $write_fh ); 

    print( "Reservation successfully removed.<br/>" ); 
}
like image 9
David W. Avatar answered Nov 09 '22 17:11

David W.


It looks like you're printing to a filehandle which you have not yet defined. At least you haven't defined it in your sample code. If you enable strict and warnings, you'll get the following message:

Name "main::NEWLIST" used only once: possible typo at remove.pl line 16.

print NEWLIST $LINE unless ($LINE =~ m/$name/);

This code should work for you:

#!/usr/bin/env perl 

use strict; 
use warnings; 

removeTime( "john" ); 

sub removeTime { 
    my $name = shift; 

    open( FILE, "<times.txt" ); 
    my @LINES = <FILE>; 
    close( FILE ); 
    open( FILE, ">times.txt" ); 
    foreach my $LINE ( @LINES ) { 
        print FILE $LINE unless ( $LINE =~ m/$name/ ); 
    } 
    close( FILE ); 
    print( "Reservation successfully removed.<br/>" ); 
}

A couple of other things to note:

1) Your sample code calls removeReservation() when you mean removeTime()

2) You don't require the round brackets in your subroutine definition unless your intention is to use prototypes. See my example above.

like image 8
oalders Avatar answered Nov 09 '22 17:11

oalders