in reply to Re: Re: head truncate
in thread head truncate

Yikes! In fact there are more bugs and more severe bugs in mine than you point out.

That the die was of course meant to have an if and not an unless clause, is only a minor problem. The seeks are cosmetics too - shame on me.

But the "late truncate" was a nasty thinko indeed. Unfortunately your fix of testing the read length against the block size won't work correctly either, since it fails when the last read gets exactly BLOCKSIZE bytes directly before the end of the file.

The cool thing about the real fix is I got rid of the if that was bothering me before. I had a gut feeling that a correct solution should not require extra tests for small files that get truncated to zero and indeed, it doesn't.

#!/usr/bin/perl -w use strict; #deletes first numbytes of a file use constant BLOCKSIZE => 128*1024; die "Usage: $0 numbytes file [file ...]\n" if @ARGV < 2; my $trunc_bytes = shift @ARGV; my $files_done = 0; for(@ARGV) { my $fh; unless(open $fh, "+<", $_) { warn "Couldn't open $_: $!\n"; next; } my $filesize = -s $fh; while($filesize > $trunc_bytes + tell $fh) { seek $fh, $trunc_bytes, 1; my $bytes_read = read($fh, my $buffer, BLOCKSIZE); seek $fh, -$bytes_read -$trunc_bytes, 1; print $fh $buffer; } truncate $fh, tell $fh; close $fh; $files_done++; } exit ($files_done == 0);

I left my old code in place for comparisons.

Update: added a couple brackets and changed open(..) or .. into unless(open ..) {..}.

Update 2: pulled my $fh out of the open.

Makeshifts last the longest.

Replies are listed 'Best First'.
Re^4: head truncate
by sauoq (Abbot) on Sep 13, 2002 at 20:03 UTC

    Much improved.

    I think exit $files_done == 0; is equivalent to exit($files_done) == 0 rather than exit ($files_done == 0) which is what you want.

    Also, ... or (warn "Couldn't open $_: $!\n", next); won't work as expected. You want ... or warn("Couldn't open $_: $!\n"), next; there. That one caught me off guard. I didn't catch it till I ran it.

    When I ran it (5.6.1), I also got this warning: Parentheses missing around "my" list at ./htr_a2 line 17. Line 17 is your read. Throwing parens around read's args eliminates the warning.

    I fixed the error you pointed out by using eof() and reintroduced the same functionality you had. I also added a check to be sure the first argument is numeric and positive.

    #!/usr/bin/perl -w use strict; use constant BLOCKSIZE => 128 * 1024; my $USAGE =<<END_OF_USAGE; Usage: $0 nbytes file [file ...] nbytes Positive integer. Number of bytes to remove. file Name(s) of file(s) to decapitate. END_OF_USAGE die $USAGE if @ARGV < 2; my $nbytes = shift @ARGV; die $USAGE unless $nbytes eq int(0+$nbytes) and $nbytes >= 0; my $completed = 0; for my $file (@ARGV) { open(my $fh, "+<", $file) or warn("Couldn't open $file: $!\n"),nex +t; if(-s $fh > $nbytes ) { seek $fh, $nbytes, 0; my $total_in = 0; while(my $bytes_in = read $fh, my $buf, BLOCKSIZE) { my $eof = eof($fh); seek $fh, $total_in, 0; print $fh $buf; $total_in += $bytes_in; last if $eof; seek $fh, $nbytes + $total_in, 0; } } truncate $fh, tell $fh; close $fh; $completed++; } exit ($completed == 0);
    -sauoq
    "My two cents aren't worth a dime.";
    

      Your fixes were the ones that came to mind when the issue first occured to me. However, I spent a while to come up with a different approach because I don't like that version for two reasons, detailed in Dominus' "red flag" articles.

      eof tries to read a byte from the handle, then rewinds the file. Basically, it's a kludge.

      Also, you need to set an $eof flag before copying, then check it later. Flag variables are not integral to the algorithm at hand and usually mean the implementation is not a natural way to express that algorithm.

      I updated my second post to fix the problems you mentioned. Only cosmetics this time, luckily.. :-)

      Makeshifts last the longest.

        eof tries to read a byte from the handle, then rewinds the file. Basically, it's a kludge.

        Yes, eof is a kludge. But it's also a supported language feature. Using it only required the addition of a line of code and a small change in another. Rather than figuring out how to eliminate it, I chose to spend my time adding error checking to the input. ;-)

        I do like your algorithm better though. ++

        Update: Putting your open in the unless block changes the scope of your $fh variable though. It'll be lexical to the unless block.

        -sauoq
        "My two cents aren't worth a dime.";