Perl diamond operator in double while loop hangs

383 views Asked by At

In my Perl script I have a double infinite while loop. I read lines from a file with the diamond operator. But somehow if my script reaches the last line of the file, it does not return undef, but hangs forever.

If I reduced my code to a single while loop this did not happen. So I wonder if I am doing something wrong or if this is a known limitation of the language. (This is actually my first perl script.)

Below is my script. It is meant to count the size of DNA sequences in fasta files, but the hanging behavior can be observed with any other file with multiple lines of text.

Perl version 5.18.2

Invoked from the commandline like perl script.pl file.fa

$l = <>;
while (1) {
    $N = 0;
    while (1) {
        print "Get line";
        $l = <>;
        print "Got line";
        if (not($l)) {
            last;
        }
        if ($l =~ /^>/) {
            last;
        }

        $N += length($l);
    }
    print $N;
    if (not($N)) {
        last;
    }
}

I put some debug print statements so that you can see that the last line printed is "Get line" and then it hangs.

2

There are 2 answers

0
lod On BEST ANSWER

Welcome to Perl.

The issue with your code is that you have no way of escaping the outer loop. <> will return undef when it reaches the end of the file. At this point your inner loop ends and the outer loop sends it back in. Forcing further reads causes <> to start looking at STDIN which never sends an EOF, so your loop continues forever.

As this is your first Perl script I'm going to rewrite it for you with some comments. Perl is a fantastic language, you can write some great code, however mostly due to it's age there are some older styles which are no longer advised.

use warnings; # Warn about coding errors
use strict; # Enforce good style
use 5.010; # Enable modernish (10 year old) features

# Another option which mostly does the same as above.
# I normally do this, but it does require a non-standard CPAN library
# use Modern::Perl;

# Much better style to have the condition in the while loop
# Much clearer than having an infinite loop with break/last statements
# Also avoid $l as a variable name, it looks too much like $1
my $count = 0; # Note variable declaration, enforced by strict
while(my $line = <>) {
    if ($line =~ /^>/) {
        # End of input block, output and reset
        say $count;
        $count = 0;
    } else {
        $count += length($line);
    }
}

# Have reached the end of the input files
say $count;
0
brucer42 On

try "echo | perl script.pl file.fa".

works for me with same "problem" in my code.

gets EOF from stdin.