http://www.perlmonks.org?node_id=1077354


in reply to Recursive Directory print

G'day zavo,

Welcome to the monastery.

"What am i doing wrong."

Quite a lot actually. Let's step through it. [A number of the issues have already been raised; I've flagged these with "issue already addressed".]

sub print_rec {

Firstly, print_rec is not a meaningful name. In a tiny script like this, you can see at a glance what the routine is supposed to be doing. In a more complex script, where sub_name() may be some screenfuls away from the sub sub_name {...} definition, this won't be at all obvious: I would probably assume that print_rec() was printing records, not directory listings.

You're passing an argument to that routine. As you'll see, you're going to need to use that argument more than once: assign it to a variable. [issue already addressed]

opendir DIR, shift;

Change shift to the variable (from previous point).

DIR is a package global variable; use a lexical variable. [issue already addressed]

You're not checking if this worked. See readdir for example code; alternatively, use the autodie pragma (my preference).

if(!/\.|\.\./ && -d $_)

The regex /\.|\.\./ matches anything with a single dot. The alternation (|\.\.) is completely pointless: if you've matched a single dot, then two dots must also match. Assuming you want to match the current and parent directories (i.e. '.' and '..'), then you'll need to anchor the pattern to the start and end of the string: /^(?:\.|\.\.)$/

-d $_ will check whether $_ is a directory in the current directory! You'll need to prefix this with the path to this filename.

return print_rec($_);

return shouldn't be here. [issue already addressed]

$_ needs a path. [issue already addressed]

elsif (!-d) { ... }

I'd just change that to else { ... } — the if is catching directories so everything else must be not a directory.

Finally, your listing should be presented in such a way that the directory structure can be easily seen. What you currently have is an unformatted list. Indentation, in much the same way as you do for your code, is a simple way to achieve this.

Putting all that together, I came up with this (heavily) modified version of your code:

#!/usr/bin/env perl -l use strict; use warnings; use autodie; use constant INDENT => ' ' x 4; my $starting_dir = '.'; print_dir_listing($starting_dir, ''); sub print_dir_listing { my ($path, $indent) = @_; opendir my $dh, $path; for (readdir $dh) { next if /^(?:\.|\.\.)$/; if (-d "$path/$_") { print "${indent}DIR: $_"; print_dir_listing("$path/$_", $indent . INDENT); } else { print "$indent$_"; } } }

In the directory from where I ran this, the listing was 2,752 lines long. Here's an extract to show what the output looks like.

... DIR: mastering_perltk_demo_code mastperltk_examples.MANIFEST mastperltk_examples.tar.gz DIR: mptk-code14 DIR: ch01 hello-world DIR: ch02 play-with-pack1 play-with-pack2 DIR: ch03 banner fontviewer DIR: ch04 ...

-- Ken