DEV Community

Gabor Szabo
Gabor Szabo

Posted on • Originally published at perlmaven.com

one-liner: read first elements of a huge directory

At a client we have a networked disk with millions of files. I was trying to list the first few files to see what's going on.

ls -l | head takes ages, but here is a Perl one-liner to make it work:

perl -E 'opendir(my $dh, "/huge_dir"); my $c = 0; while (my $d = readdir($dh)) { say $d; exit if ++$c > 3 }'
Enter fullscreen mode Exit fullscreen mode

In a different layout that could be put in a file

use feature 'say';
opendir(my $dh, "/huge_dir");
my $c = 0;
while (my $d = readdir($dh)) {
    say $d;
    exit if ++$c > 3
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

perl -E tells perl that the next string is a piece of perl code that should executed. (as opposed to being a filename) This is what let's us write one-liners in Perl. Using the capital letter -E turns on the feature say among other things.

opendir(my $dh, "/huge_dir"); opens a directory and assigns the directory handle to the new variable called $dh that was declared on the spot with my

my $c = 0; declares a variable called $c and assign 0. We'll use this for counting the entries.

while (my $d = readdir($dh)) a while loop that for each iteration will read one entry from the directory and assign it to the newly declared variable $d. We declare this variable inside the loop to make it scoped to the loop.

{ say $d; exit if ++$c > 3 } the block of the while loop. First statement prints the value of $d followed by a newline. Then we have a conditional statement in what is called statement modifier where the statement comes before the condition. We first increment the counter and then check if it is greater than 3 and if it is then we call exit.

I was even a bit overdoing with declarations. If we are not using strict we don't need to declare variables. If a variable does not even have value but is incremented using ++ it will behave as if there was a 0 in it. So we don't need to initialize $c.

perl -E 'opendir($dh, "/huge_dir"); while ($d = readdir($dh)) { say $d; exit if ++$c > 3}'
Enter fullscreen mode Exit fullscreen mode
use feature 'say';
opendir($dh, "/huge_dir");
while ($d = readdir($dh)) {
    say $d;
    exit if ++$c > 3
}
Enter fullscreen mode Exit fullscreen mode

And I don't need that $d variable either. Instead of that I can use the invisible $_:

perl -E 'opendir($dh, "/huge_dir"); while (readdir($dh)) { say; exit if ++$c > 3}'
Enter fullscreen mode Exit fullscreen mode
use feature 'say';
opendir($dh, "/huge_dir");
while (readdir($dh)) {
    say;
    exit if ++$c > 3
}
Enter fullscreen mode Exit fullscreen mode

Top comments (3)

Collapse
 
ap profile image
Aristotle Pagaltzis
perl -Mautodie -E 'opendir DH, "/huge_dir"; for ( 1 .. 3 ) { say readdir 
DH // last }'
Enter fullscreen mode Exit fullscreen mode
  1. If you’re using globals anyway, might as well use a glob instead of a glob ref and save a $ or two.
  2. If you want a loop that will stop at condition X but also after Y iterations then it’s usually more concise to use a for loop over the iteration count and abort on the other condition, rather than use a while loop on the condition and abort it on an iteration counter check.
  3. You weren’t checking for opendir errors. (Yes, it’s just a one-liner where you know it will work. But then why use while (readdir)? You know there’s more than 3 entries in that directory, too.) Throwing in an -Mautodie is a quick way to absolve a lot of one-liners of explicit error checking. For a handful of keystrokes you get to buy a clean conscience.
Collapse
 
jrw profile image
jrw

You could also use a (postfix) statement modifier for the for loop.

perl -Mautodie -E 'opendir DH, "/huge_dir"; say readdir DH // last for 1..3'
Enter fullscreen mode Exit fullscreen mode
Collapse
 
szabgab profile image
Gabor Szabo

Nice improvement!