问题
First off, I don't have the ability to use File::Find.
So I have my script to walk through directories and find a certain type of file. But if I go more than one sub-directory deep, my script doesn't properly exit all the way back up to the starting directory. I think I need to have a $previousDir variable that keeps track of the last directory so I can say to go back out to that one when I'm done in the sub-directory. But I've tried putting it in multiple places without success...
File Structure (BOLD is a Dir, Italic is a file):
startingdirectory/Logs - AAA, Dir1, zzz, adstatlog.299, adstatlog.tgz, file
/AAA - filefile
/Dir1 - /Dir2, config.tar.gz
/Dir2 - EMPTY
/zzz - withinzzz
Here is my current script:
# specify the directory where you want to start the search
my $startingDir = $ARGV[0];
my $directoryCount = 0;
my $directory = shift;
my $previousDir;
my @directories;
my $tarOutput;
# Calling the Subroutine, which searches the Directory
readDirectory($startingDir);
sub readDirectory
{
# Open and close the startingDir
opendir(DIR, @_[0]) or die("ERROR: Couldn't open specified directory $!");
my @files = grep { $_ !~ /^\.{1,2}$/ } readdir DIR;
closedir DIR;
print "------------------------------------------------------------------------\n\n";
foreach my $currentFile (@files)
{
print "Current File: ", $currentFile, "\n\n";
#Directory currently searching through
print "Searching in $directory\n\n";
my $fullPath = "$directory/$currentFile";
print "FULL PATH: $fullPath\n\n";
if ( -d $fullPath )
{
print "Found New Directory: ", $currentFile, "\n\n";
push (@directories, $currentFile);
$directoryCount++;
print "Current number = $directoryCount\n\n";
print "Directories: @directories \n\n";
$previousDir = $directory;
$directory = $fullPath;
# The Subroutine is calling hisself with the new parameters
readDirectory($directory);
}
elsif ( $currentFile =~ /\.tar.gz$/i || $currentFile =~ /\.tar$/i || $currentFile =~ /\.tgz$/i)
{
print "File: ", $currentFile, "\n\n";
my $tarOutput = `tar -tvzf $currentFile`;
print $tarOutput, "\n";
$previousDir = $directory;
}
print "PREVIOUSDIR: $previousDir\n\n";
print "-----------------------------------------------------------------------\n\n";
$directory = $previousDir;
}
}
And the output: (scroll down to see where issue begins)
------------------------------------------------------------------------
Current File: AAA
Searching in /home/gackerma/Logs
FULL PATH: /home/gackerma/Logs/AAA
Found New Directory: AAA
Current number = 1
Directories: AAA
------------------------------------------------------------------------
Current File: filefile
Searching in /home/gackerma/Logs/AAA
FULL PATH: /home/gackerma/Logs/AAA/filefile
PREVIOUSDIR: /home/gackerma/Logs
------------------------------------------------------------------
PREVIOUSDIR: /home/gackerma/Logs
------------------------------------------------------------------
Current File: Dir1
Searching in /home/gackerma/Logs
FULL PATH: /home/gackerma/Logs/Dir1
Found New Directory: Dir1
Current number = 2
Directories: AAA Dir1
------------------------------------------------------------------------
Current File: DIR2
Searching in /home/gackerma/Logs/Dir1
FULL PATH: /home/gackerma/Logs/Dir1/DIR2
Found New Directory: DIR2
Current number = 3
Directories: AAA Dir1 DIR2
------------------------------------------------------------------------
PREVIOUSDIR: /home/gackerma/Logs/Dir1
------------------------------------------------------------------
Current File: configs.tar.gz
Searching in /home/gackerma/Logs/Dir1
FULL PATH: /home/gackerma/Logs/Dir1/configs.tar.gz
PREVIOUSDIR: /home/gackerma/Logs/Dir1
------------------------------------------------------------------
PREVIOUSDIR: /home/gackerma/Logs/Dir1 ***THIS IS WHERE THE ISSUE STARTS -
PREVIOUSDIR SHOULD BE /Logs!!***
------------------------------------------------------------------
Current File: file
Searching in /home/gackerma/Logs/Dir1
FULL PATH: /home/gackerma/Logs/Dir1/file
PREVIOUSDIR: /home/gackerma/Logs/Dir1
------------------------------------------------------------------
Current File: adstatlog.299
Searching in /home/gackerma/Logs/Dir1
FULL PATH: /home/gackerma/Logs/Dir1/adstatlog.299
PREVIOUSDIR: /home/gackerma/Logs/Dir1
------------------------------------------------------------------
Current File: zzz
Searching in /home/gackerma/Logs/Dir1
FULL PATH: /home/gackerma/Logs/Dir1/zzz
PREVIOUSDIR: /home/gackerma/Logs/Dir1
------------------------------------------------------------------
Current File: adstatlog.tgz
Searching in /home/gackerma/Logs/Dir1
FULL PATH: /home/gackerma/Logs/Dir1/adstatlog.tgz
PREVIOUSDIR: /home/gackerma/Logs/Dir1
------------------------------------------------------------------
回答1:
I would really use File::Find if you can.
Here's a working, simplified version of your recursive try:
use warnings;
use strict;
die "Usage: $0 (abs path to dir) " if @ARGV != 1;
my $dir = shift @ARGV;
file_find($dir);
sub file_find {
my $dir = shift;
opendir my $dh, $dir or warn "$dir: $!";
my @files = grep { $_ !~ /^\.{1,2}$/ } readdir $dh;
closedir $dh;
for my $file ( @files ) {
my $path = "$dir/$file";
if( $path =~ /(\.tar\.gz|\.tar|\.tgz)$/ ) {
print "do tar for $path\n";
}
file_find($path) if -d $path;
}
}
回答2:
The File::Find module has been a standard Unix module since Perl 5.000. In fact, it's been a standard module since Perl 3.x, maybe even before. In fact, I have Perl 5.12 installed on my Mac, and I still see the old find.pl file sitting in one of the @INC directories.
Back before Perl 5 (or maybe even before Perl 4), you'd do this:
require "find.pl";
instead of
use File::Find;
TO get the find command on your system (find.pl is there for backwards compatibility). This is why I find it so hard to believe you don't have File::Find on your system. It'd be like saying you don't have the dir command on your Windows PC.
Run the command perl -V. That's a capital V. This will print out the @INC directory list. See if you can find a File directory in only of those directories listed in that list. Under that directory should be a Find.pm Perl module.
Here's what it looks like on my PC running Strawberry Perl:
@INC:
C:/perl/perl/site/lib
C:/perl/perl/vendor/lib
C:/perl/perl/lib
.
On my Mac, 10 directories are listed in that @INC list.
Also see which version of Perl you have on your system. And, make sure the directories listed in @INC are readable by you.
There is something definitely wrong with your Perl installation if you don't have File::Find on your system. I'd be more worried about that than File::Find itself.
One more thing, see if you have perldoc command installed. If you do, type:
$ perldoc File::Find
and see if that gives you any documentation on File::Find. If it does, it means that File::Find is on your system. Then run:
$ perldoc -l File::Find
which will give you the location of the File::Find module.
Before doing anything else, verify that File::Find really, really doesn't exist on your system, or that you don't have read access to it before doing anything else. As I said before, if this module doesn't exist on your system, you have major problems with your Perl installation, and I'd be worried whether it can be trusted. This needs to be resolved.
If everything is okay, then we need to see your program to figure out why you can't use File::Find. It might be something minor, like using quotes around the program's name.
回答3:
There are a number of problems with your program. The main error is that you are using too many global variables and trying to manually keep them in synch with the directory you are currently processing.
Here is a list
Always
use strictanduse warningsfor every program you writewarningswould have told you that you should writeopendir(DIR, $_[0])instead ofopendir(DIR, @_[0])You are setting
$directoryto$previousDirafter every entry in a directory. But$previousDiris being set only when the current entry is another directory, so after ordinary files the value is restored even though it hasn't been saved.You are getting confused about whether you should be reading the directory specified by global variable
$directoryor by the parameter passed to the subroutine.
By far the easiest way to do this is to use only the subroutine parameter to specify the current directory and forget about the global variable. Here is a program that does what yours is intended to
use strict;
use warnings;
process_dir($ARGV[0]);
sub process_dir {
my ($dir) = @_;
opendir my $dh, $dir or die $!;
my @entries = grep { not /^\.\.?$/ } readdir $dh;
closedir $dh;
for my $entry (@entries) {
my $fullname = "$dir/$entry";
if (-d $fullname) {
process_dir($fullname);
}
elsif ($entry=~ /(?:\.tar|\.tgz|\.tar\.gz)$/i)
print "File: ", $fullname, "\n\n";
print `tar -tvzf $fullname`;
}
}
}
来源:https://stackoverflow.com/questions/16671127/perl-directory-walking-issue-cant-go-back-up-more-than-1-directory-properly