View Full Version : how to drill further down a dir.
Hi,
below is my code which loops through a parent dir, to show the names of 1st level sub dirs.
What I want to do is to output the names of the second level sub dirs.
Should I run a new (additional) foreach routine or find a module that would be more efficient?
my $Filepath = "/domains/574/1617/html/FILES/";
my ( @listOfDirs );
tie my %files, 'IO::Dir', $Filepath;
foreach my $key ( keys %files ) {
next if ( substr($key, 0, 1) eq '.' );
my $isItADir = ( $files{$key}->mode & 0x004000 ) >> 14;
if ( $isItADir == 1 ) {
push @listOfDirs, $key;
}
} # end of foreach
if ( @listOfFiles > 0 || @listOfDirs > 0 ) {
foreach my $key ( sort @listOfDirs ) {
my $fileSubject = "$key";
$fileSubject =~ s/_/ /g;
if ($key eq "$baseBusinessType" || $key eq 'DIRECTORIES' || $key eq 'Directory' || $key eq 'Apartments' || $key eq 'Rooms'){}
elsif ($key eq "Client_Details"){} else{
print <<EOF;
<li><a href="/cgi-bin/nearby?baseBusiness=$baseBusiness&category=$key">$fileSubject </a></li>
EOF
}
}
}
Bazz
FishMonger
12-06-2005, 04:42 PM
Didn't we discusses this some time ago? How many sub dirs do you have or expect to have in the parent dir? Do each of those sub dirs have sub dirs? How many levels do you want to desend?
Use File::Find instead of IO::Dir
http://search.cpan.org/~nwclark/perl-5.8.7/lib/File/Find.pm
EDIT:
BTW, your current foreach loop could be simplified to this:
while(<$Filepath/*>) { push @ListOfDirs, $_ if -d $_; }
Thank you Fishmonger, I'll take a look into that.
One parent dir, and each subDir containing one level of subsubdirs.
Bazz
ugh! I don;t seem to be getting this at all :(
sub nearby {
use File::Find;
find(\&wanted, @directories);
sub wanted {
$File::Find::dir = '/domains/574/1617/html/FILES/';
print <<EOF;
$File
EOF
}
} # end of sub routine
The path is correct. but the error I get is:
<H1>Software error:</H1>
<PRE>invalid top directory at /usr/lib/perl5/5.6.1/File/Find.pm line 295.
</PRE>
<P>
For help, please send mail to the webmaster (<a href="mailto:webmaster@mydomain.co.uk">webmaster@mydomain.co.uk</a>), giving this error message
and the time and date of the error.
Content-type: text/html
<H1>Software error:</H1>
<PRE>[Thu Dec 8 12:03:36 2005] mainpage2: invalid top directory at /usr/lib/perl5/5.6.1/File/Find.pm line 295.
Compilation failed in require at /domains/920/2274/html/cgi-bin/mainpage2 line 81.
</PRE>
<P>
For help, please send mail to the webmaster (<a href="mailto:webmaster@mydomain.co.uk">webmaster@mydomain.co.uk</a>), giving this error message
and the time and date of the error.
please help.
Bazz
FishMonger
12-08-2005, 05:00 PM
See if this helps to understand how it works.
use File::Find;
$dir = '/domains/574/1617/html/FILES/';
find(\&wanted, $dir);
sub wanted {
if ( -d $_ ) { # we want only directories
print "directory name: $_\n";
print "directory path: $File::Find::dir\n";
print "directory name with full path: $File::Find::name\n\n";
}
}
EDIT:
Using a here document to print a single line does not make sense.
Ahhh I have learned some more. Thank you FishMonger.
Does Anyone know of a good tutorial, which can help me learn how to use it even more? I want to do other things with it.
I want to perform a different function for each level of directory.
I want to control the depth to which it runs down a dir structure (2 3 or more levels),
I want to append html for each layer separately. (dd list).
But I want to learn rather than trouble one of you guys, to do it for me.
Bazz
hyperbole
12-08-2005, 09:03 PM
Try File::Find::Rule. It's a different apporach to the same thing.
.
Thanks for that. I'll check that out but I still need to resolve a question:
I want to perform a different function for each level of directory.
here is my code presently which nearly does what I want. Unfortunately, where there are a dozen or so accommodation, it outputs 'acconmmodation' separately each time. If the term appears once or more, I want it to output only once.
I really appreciate your help so far and think I'm nearly there.
use File::Find;
$dir = '/domains/574/1617/html/FILES/';
find(\&wanted, $dir);
print <<EOF;
<ul>
EOF
sub wanted {
if ( follow => 1, -d $_ ) { # we want only directories
#print "<li>$_ </li>\n";
my $businessDetails = $File::Find::name;
my ( $blank, $domains, $path1, $path2, $html, $FILES, $subject, $nearbyBusinessFileName ) = split /\// , $businessDetails , 9;
my ($nearbyBusinessName, $nearbyBusinessType, $nearbyBusinessSubType, $nearbyBusinessCat, $nearbyBusinesslocalRegion, $nearbyBusinessCounty, $nearbyBusinessGrid, $nearbyBusinessPostCode, $nearbyBusinessGroupName, $nearbyBusinessTIC) = split /\_/, $nearbyBusinessFileName, 11;
if ($nearbyBusinessTIC) {
%hash = ( "$nearbyBusinessName", "$nearbyBusinessType", "$nearbyBusinessSubType", "$nearbyBusinessCat", "$nearbyBusinesslocalRegion", "$nearbyBusinessCounty", "$nearbyBusinessGrid", "$nearbyBusinessPostCode", "$nearbyBusinessGroupName", "$nearbyBusinessTIC" );
%hash = (
$nearbyBusinessType => "$nearbyBusinessSubType",
);
while ( my ($key, $value) = each(%hash) ) {
print "$key => $value <br />\n";
}
}
#print "directory name with full path: $File::Find::name\n\n";
}
print <<EOF;
</ul>
EOF
}
}
Bazz
FishMonger
12-09-2005, 06:08 PM
Let me start by asking 2 questions.
Are some of your directory names symbolic links? If not, then there's no need to use follow => 1.
Why do first create the hash by splitting $nearbyBusinessFileName and then the very next line you over write it?
%hash = ( "$nearbyBusinessName", "$nearbyBusinessType", "$nearbyBusinessSubType", "$nearbyBusinessCat", "$nearbyBusinesslocalRegion", "$nearbyBusinessCounty", "$nearbyBusinessGrid", "$nearbyBusinessPostCode", "$nearbyBusinessGroupName", "$nearbyBusinessTIC" );
is overwritten by: %hash = (
$nearbyBusinessType => "$nearbyBusinessSubType",
);
Now, the only realistic answer I can give to your question is one that I've given several times before. Use a database. You are spending a far greater amount of time and effort by using your current approach. Also, due to this approach, your code is getting very convoluted and obfuscated.
heh, I know you said before about using a Db. When I have finshed this script, I can take a break from it to learn MySQL.
To answer your questions,
I was trying out the follow=> 1, to see if I could stop the looping through more than two levels of directories, but it seems to make no difference.
I didn't notice my mistake with the hash. I was breaking up a dir name on the _ and then putting two of the values from that into the hash.
tidier code, so far.
use File::Find;
$dir = '/domains/574/1617/html/FILES/';
find(\&wanted, $dir);
sub wanted {
if ( -d $_ ) { # we want only directories
my $businessDetails = $File::Find::name;
my ( $blank, $domains, $path1, $path2, $html, $FILES, $subject, $nearbyBusinessFileName ) = split /\// , $businessDetails , 9;
my ( $nearbyBusinessName, $nearbyBusinessType, $nearbyBusinessSubType, $nearbyBusinessCat, $nearbyBusinesslocalRegion, $nearbyBusinessCounty, $nearbyBusinessGrid, $nearbyBusinessPostCode, $nearbyBusinessGroupName, $nearbyBusinessTIC) = split /\_/ , $businessDetails ;
%hash = (
$nearbyBusinessType => "$nearbyBusinessSubType",
);
if ($nearbyBusinessTIC) {
print <<EOF;
<li>$nearbyBusinessType<li>$nearbyBusinessSubType</li></li>
EOF
}
}
}
#print "directory name with full path: $File::Find::name\n\n";
print <<EOF;
</ul>
EOF
FishMonger
12-10-2005, 04:56 PM
Instead of the follow => 1 you need to look at the $File::Find::prune = 1 setting, however, I haven't had much success with it, but that might be because the module may not be fully compatible with the Windows platform that I required.
I haven't worked up any code, but I have an idea that's only partially worked that you could work on. Create separate subroutines for the functions that you want to perform at each level and create a hash where the keys are the “names of the levels” and their values are references to the subroutines. Assuming that the top level directories are in @listOfDirs then $File::Find::prune = 1 will descend only into 1 level below each of those which is the second level. All that is needed is to “call” the appropriate hash key to execute the appropriate subroutine within each level.
----------
Again, why do you do this:
print <<EOF;
<li>$nearbyBusinessType<li>$nearbyBusinessSubType</li></li>
EOF
}or this:print <<EOF;
</ul>
EOFWhen this is cleaner and easier?print "<li>$nearbyBusinessType<li>$nearbyBusinessSubType</li></li>";print '</ul>';
If you really need the line breaks in the html source code, you could simple include \n in the print statement or use the CGI::Pretty module.
One of the advantages of Perl and what most experienced Perl programmers strive for is to use as few key strokes as possible, which is something that I don't work hard enough at!
Thanks FishMonger.
A for the WHY?? well, its probaly a hang over from the not-to-distant-past, when I found the <<EOF; way, was easier to follow. It, sort of, made it easier not to mix up perl code and html at the time when I 'always' got the error 500 page :( However, I get what you suggest about fewer keystrokes.
someday, maybe I'll get the hang of this. :rolleyes:
Bazz
hyperbole
12-10-2005, 07:19 PM
use File::Find::Rule;
my @files = File::Find::Rule->maxdepth($max_level)->in(@directory_list);
Thank you very much, hyperbole. I'll try that.
Bazz
vBulletin® v3.8.2, Copyright ©2000-2012, Jelsoft Enterprises Ltd.