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

I'm not a great coder. I can usually get things done by using a combination of perl & unix utils, but I'm trying to do a project to help teach myself more. The part I'm working on now is stepping through the filesystem, starting at / listing the directory, and for each item, testing if its a directory or a file, if it's a file it does a stat() and if it's a directory, it lists it and does the same process over again.

The problem is, instead of listing the subdirectories, it combines the parents list with the childs.

For example: listing root gives "bin, boot ... var" instead of listing /bin/ it tries to list /bin/bin, /bin/boot ... /bin/var/

I've been looking at this for hours, and every time i think I make progress I end up going two steps back.

I'm sure the problem will be obvious to someone more versed than I am, and I would appreciate the help. I haven't cleaned this up too much, but if you've got any comments on general coding style, etc. please let me know, I'm here to learn, I don't care if I come off like an idiot.

thanks in advance,
Nick

#! /usr/bin/perl use Digest::MD5 qw(md5 md5_hex md5_base64); use strict; use diagnostics; use warnings; our $debug = 0; sub dir { print "----------------- dir($_[0]) -----------------\n"; if ( !defined( $_[0] ) ) { die("Nothing passed to the dir function\n"); } my $path = $_[0]; foreach my $file ( lsdir("/") ) { #--------------------------------------------------------- # Step through the results of the lsdir # and determine if it is a) a directory # or b) a file. For each one, run it's # respective function. #--------------------------------------------------------- print "$file\n" if ( 0 == $debug ); #my $holder = "$path/$file/"; #print "HOLDER:\t$holder\n" if ( 0 == $debug ); my $isdir_result = isdir("$path/$file") if ( 0 == $debug ); print "ISDIR RESULTS:\t$isdir_result\n" if ( 0 == $debug ); if ( 0 == isdir("$path/$file") ) { print "Dir:\t/$path/$file/\n"; if ( '/' eq $path ) { # if it's the root directory, just use the file print "$file is a directory.\n" if ( 0 == $debug ); print "instance 1: dir($file)\n"; dir("/$file"); } else { #otherwise, just use the path print "$file is a file.\n" if ( 0 == $debug ); print "instance 2: dir($file)\n"; if ( $file !~ m/^\// ){ $file = "/" . $file; } dir($path); } } elsif ( 1 == isdir("$path/$file") ) { print "File:\t/$file\n"; my %file_info = file("/$file"); if ( 0 == $debug ) { foreach my $key ( keys %file_info ) { } } } } } sub file { print "o0o0o0o0o0o file($_[0]) o0o0o0o0o0o\n"; my $file = $_[0]; my %attrib; $attrib{name} = $file; # stat file ( $attrib{dev}, $attrib{ino}, $attrib{mode}, $attrib{nlink}, $attrib{uid}, $attrib{gid}, $attrib{rdev}, $attrib{size}, $attrib{atime}, $attrib{mtime}, $attrib{ctime}, $attrib{blksize}, $attrib{blocks} ) = stat($file); # compute md5 open( FILE, $file ) or print "Couldn't open $file!"; if ( -e $file ) { binmode(FILE); my $md5 = Digest::MD5->new; while (<FILE>) { $md5->add($_); } close(FILE); $attrib{md5} = $md5->b64digest; } else { print "What the hell.... $file doesn't exist?\n"; } # give it back return (%attrib); } sub isdir { print "/\/\/\/\/\/\ isdir() /\/\/\/\/\/\ \n"; print "isdir($_[0])\n"; my $file = $_[0]; if ( -d "$file" ) { print "ISDIR = YES\n" if ( 0 == $debug ); return(0); } else { print "ISDIR = NO\n" if ( 0 == $debug ); return(1); } } sub lsdir { print "************ lsdir($_[0]) **************\n"; my $dir = $_[0]; my @files; opendir( DIRHANDLE, "$dir" ) || die "Cannot opendir /some/path +: $!"; foreach my $file ( sort readdir(DIRHANDLE) ) { push( @files, $file ) unless ( $file =~ m/^(\.){1,2}$/ ); print $file, "\n" if ( 2 == $debug ); } return @files; } my $path = "/"; print $path; dir("$path"); #|| die( "Couldn't run dir($path)");
Output:

/----------------- dir(/) ----------------- ************ lsdir(/) ************** bin ////// isdir() ////// isdir(//bin) ISDIR = YES ISDIR RESULTS: 0 ////// isdir() ////// isdir(//bin) ISDIR = YES Dir: ///bin/ bin is a directory. instance 1: dir(bin) ----------------- dir(/bin) ----------------- ************ lsdir(/) ************** bin ////// isdir() ////// isdir(/bin/bin) ISDIR = NO ISDIR RESULTS: 1 ////// isdir() ////// isdir(/bin/bin) ISDIR = NO ////// isdir() ////// isdir(/bin/bin) ISDIR = NO File: /bin o0o0o0o0o0o file(/bin) o0o0o0o0o0o boot ////// isdir() ////// isdir(/bin/boot) ISDIR = NO ISDIR RESULTS: 1 ////// isdir() ////// isdir(/bin/boot) ISDIR = NO ////// isdir() ////// isdir(/bin/boot) ISDIR = NO File: /boot o0o0o0o0o0o file(/boot) o0o0o0o0o0o dbmsrv.prt ////// isdir() ////// isdir(/bin/dbmsrv.prt) ISDIR = NO ISDIR RESULTS: 1 ////// isdir() ////// isdir(/bin/dbmsrv.prt) ISDIR = NO ////// isdir() ////// isdir(/bin/dbmsrv.prt) ISDIR = NO File: /dbmsrv.prt o0o0o0o0o0o file(/dbmsrv.prt) o0o0o0o0o0o dev ////// isdir() ////// isdir(/bin/dev) ISDIR = NO ISDIR RESULTS: 1 ////// isdir() ////// isdir(/bin/dev) ISDIR = NO ////// isdir() ////// isdir(/bin/dev) ISDIR = NO File: /dev o0o0o0o0o0o file(/dev) o0o0o0o0o0o etc

2004-11-25 Edited by Arunbear: Changed title from 'My brain hurts', as per Monastery guidelines

Replies are listed 'Best First'.
Re: Directory traversal problem
by Zaxo (Archbishop) on Nov 25, 2004 at 02:56 UTC

    It probably won't ease the pain at first, but File::Find is designed for recursing through directory trees.

    You can get a hashed stat with File::stat, whose stat returns a reference to a hash like you produce.

    Also notable when dealing with filenames and directory trees are File::Basename and File::Spec.

    After Compline,
    Zaxo

Re: Directory traversal problem
by Eimi Metamorphoumai (Deacon) on Nov 25, 2004 at 02:01 UTC
    Without looking too deeply into it, it looks like the problem is in dir, where you have
    foreach my $file ( lsdir("/") ) {
    So in each directory, it's going ove the contents of the root again. It looks to me like you meant
    foreach my $file ( lsdir($path) ) {
    Also, stylistically, you've got some very strange things. You seem to be using 0 for true, and 1 for false, and then doing a lot of if (0 == $debug) instead of doing a more conventional style of using 1 for true and either 0 or undef for false. That way, your tests could just be if ($debug) (same for isdir, and possibly others). And you're calling isdir several times, instead of (actually, in addition to) just storing the value returned the first time. It'd be much more natural to do
    if (isdir("$path/$file"){ ... } else { ... }
Re: Directory traversal problem
by zentara (Cardinal) on Nov 25, 2004 at 14:00 UTC
    This might help you simplify your life:
    #!/usr/bin/perl use warnings; use strict; use File::Slurp::Tree; # The tree datastructure is a hash of hashes. The keys of # each hash are names of directories or files. Directories # have hash references as their value, files have a scalar # which holds the contents of the file. my $dir = shift || '.'; my %tree; my $tree = slurp_tree($dir); my $depth = 0; print "$dir\n"; print_keys($tree); sub print_keys { my $href = shift; $depth++; foreach ( keys %$href ) { print ' ' x $depth, "--$_\n"; print_keys( $href->{$_} ) if ref $href->{$_} eq 'HASH'; } $depth--; }

    I'm not really a human, but I play one on earth. flash japh