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

Hello,

I am trying to remove the last line of many files, but only if that last line is a blank line. What I have here will truncate any last line, which I would like to revise.

#! usr/bin/perl opendir(FILES,".")||die "Cannot open files in the directory\n"; @files=(); for(readdir(FILES)){ if($_=~/\.txt/){ push(@files, $_); } } print "@files\n"; for $i(0..$#files){ print "working on $files[$i]\n"; open (FH, "+<$files[$i]") or die "can't update $file: $!"; while ( <FH> ) { $addr = tell(FH) unless eof(FH); } truncate(FH, $addr) or die "can't truncate $file: $!"; } exit;
I can see that $addr is a position pointer, while what I'm interested in is the content of the last line...

The good thing about the above code is that it's very fast. Each of the file I have contains more than 30K lines, and I have hundreds of them, so I really don't want to do this by hand.

I would appreciate any help with this...
Thank you,

Hyunjin Choi

Replies are listed 'Best First'.
Re: truncate only a last blank line
by choedebeck (Beadle) on Dec 28, 2005 at 22:57 UTC
    Keeping your code mostly the same you could do something like this.
    #! usr/bin/perl opendir(FILES,".")||die "Cannot open files in the directory\n"; @files=(); for(readdir(FILES)){ if($_=~/\.txt/){ push(@files, $_); } } print "@files\n"; for $i(0..$#files){ print "working on $files[$i]\n"; open (FH, "+<$files[$i]") or die "can't update $file: $!"; my $linevalue; while ( $linevalue = <FH> ) { $addr = tell(FH) unless eof(FH); last if (eof(FH)); } if($linevalue eq "\n") { truncate(FH, $addr) or die "can't truncate $file: $!"; } exit;

    There are probably better ways to do this, but I don't see why this one wouldn't work.
      Thank you so much for your reply.
      I really don't know how to thank you enough!
      This script did exactly what I needed to do.
      I modified it a little bit at the end.
      Thank you again!

      #! usr/bin/perl opendir(FILES,".")||die "Cannot open files in the directory\n"; @files=(); for(readdir(FILES)){ if($_=~/\.txt/){ push(@files, $_); } } print "@files\n"; for $i(0..$#files){ print "working on $files[$i]\n"; open (FH, "+<$files[$i]") or die "can't update $file: $!"; my $linevalue; while ( $linevalue = <FH> ) { $addr = tell(FH) unless eof(FH); last if (eof(FH)); } if($linevalue =~ /^\s*$/) { truncate(FH, $addr) or die "can't truncate $file: $!"; print "truncated the last blank line in $files[$i]\n"; }else{ print "$files[$i] does not end with blank line\n"; } } exit;
Re: truncate only a last blank line
by swampyankee (Parson) on Dec 28, 2005 at 23:56 UTC

    It's fast, but doesn't do what you want..

    This may be a silly question, but what about using the File::ReadBackwards module? It has a 'tell' function, which should give you the value for the file pointer at the beginning of the last line which has been read. With this information, use truncate to trim off the unwanted line. (When I get a test case, I'll post my (probably hideously ugly) sample code)

    Alternatively, you could do this (which qualifies as ugly)

    open(FILE, $file) or die "$file could not be opened because $!\n"; @slurp = <FILE>; close(FILE); pop(@slurp) if $slurp[-1] =~ /^\s*$/; open(FILE, "> $file") or die "$file could not be opened because $!\n"; print FILE @slurp; close(FILE);

    I did say it was ugly...

    emc

    " When in doubt, use brute force." — Ken Thompson
Re: truncate only a last blank line
by smokemachine (Hermit) on Dec 28, 2005 at 23:23 UTC
    can be this:
    #!/usr/bin/perl while(<./*.txt>){ print "working on $_\n"; open (FH, "+<$_") or die "can't update $file: $!"; $l=$_ while <FH>; truncate FH, tell(FH) - 1 if $l =~ /^$/; }
Re: truncate only a last blank line
by Aristotle (Chancellor) on Dec 29, 2005 at 16:31 UTC

    No need to seek through the file. You already know that what you want to get rid of, if it’s there at all, is at the very end.

    #! usr/bin/perl use strict; use warnings; # length of end-of-line marker my $eol_len = length $/; opendir my $dh, "." or die "Can't open current directory: $!\n"; while( my ( $file ) = readdir $dh ) { next if $file !~ /\.txt/; open my $fh, '+<', $file or warn( "Can't open $file for read/write: $!\n" ), next; # read data from last possible location of end-of-line seek $fh, -$eol_len, 2; my $tail = <$fh>; # if chomp finds an EOL, rewind and truncate if( chomp $tail ) { seek $fh, -$eol_len, 2; truncate $fh, tell $fh; } }

    Makeshifts last the longest.

Easy if you don't mind slurping.
by kbrint (Sexton) on Dec 29, 2005 at 22:16 UTC
     perl -pi.bak -e 'BEGIN { $/ = undef }; s{ ^ \n \z }{}xms' *.txt

      Neat.

      And you can shorten that some:

      perl -pi.bak -0777e's{ ^ \n \z }{}xms' *.txt

      Makeshifts last the longest.

Re: truncate only a last blank line
by tcf03 (Deacon) on Dec 29, 2005 at 19:04 UTC
    #!/usr/bin/perl -w use strict; my ($myfile, @LINES); open $myfile, "file" or die "Unable to open file: $!\n"; push @LINES, $_ for (<$myfile>); #print "$_" for @LINES; chomp ($LINES[$#LINES-1]) if ( $LINES[$#LINES] =~ m/\s+/ ); #print "$_" for @LINES;
    or
    #!/usr/bin/perl my @file = qx/cat file/; chomp ($file[$#file]) if $file[$#file] =~ m/\s+/;

    worked for me.
    Ted
    --
    "That which we persist in doing becomes easier, not that the task itself has become easier, but that our ability to perform it has improved."
      --Ralph Waldo Emerson
      OT: I once had need to remove the first line from a file and did it with this shell script...
      # this once had a purpose... LINES=$(wc -l $1 | awk '{print $1}') tail -$(echo $LINES - 1 | bc -l) $1
      Ted
      --
      "That which we persist in doing becomes easier, not that the task itself has become easier, but that our ability to perform it has improved."
        --Ralph Waldo Emerson
Re: truncate only a last blank line
by tcf03 (Deacon) on Dec 30, 2005 at 03:14 UTC
    Array::LineReader looks promising...

    Ted
    --
    "That which we persist in doing becomes easier, not that the task itself has become easier, but that our ability to perform it has improved."
      --Ralph Waldo Emerson