【问题标题】:Perl Directory Walking issue - Can't go back up more than 1 directory properlyPerl 目录遍历问题 - 无法正确备份超过 1 个目录
【发布时间】:2013-05-21 13:20:09
【问题描述】:

首先,我无法使用 File::Find。

所以我有我的脚本来遍历目录并找到某种类型的文件。但是,如果我深入一个以上的子目录,我的脚本就不会一直正确退出到起始目录。我想我需要有一个 $previousDir 变量来跟踪最后一个目录,这样当我在子目录中完成时我可以说回到那个目录。但我试过把它放在多个地方都没有成功......

文件结构(BOLD是目录,斜体是文件):

起始目录/日志 - AAADir1zzzadstatlog.299、adstatlog.tgz、文件

/AAA - 文件文件

/Dir1 - /Dir2config.tar.gz

/Dir2 - 空

/zzz - withinzzz

这是我当前的脚本:

# 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;
    }   
}

输出:(向下滚动查看问题开始的位置)

------------------------------------------------------------------------

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

------------------------------------------------------------------

【问题讨论】:

  • 你没有能力使用File::Find没有多大意义,但你认为你有能力重新实现它。你的时间本来可以花在学习这个模块上的。有一个 find2perl 命令可以从 unix find 语法生成 File::Find 代码,并让您更好地了解如何使用它。
  • 好吧,实际上,显然我可以。同事正在将模块安装在我无权访问的备份服务器上。我仍然希望我知道如何手动操作.....
  • 它是一个核心模块,除非您使用的是旧版本的 perl,否则不需要安装它。
  • Wellllllll 当我尝试将它包含在我得到的脚本中时:Can't locate File/Find/Rule.pm in @INC (@INC contains: /usr/local/lib64/perl5 / usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 .) at tarSearch.pl line 4. BEGIN failed - 编译中止在 tarSearch.pl 第 4 行。
  • 那是因为File::Find::Rule 不是核心模块。 File::Find 很棒,而且易于使用。你应该试试看。

标签: perl search directory


【解决方案1】:

如果可以的话,我真的会使用File::Find

这是递归尝试的一个有效的简化版本:

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;
    }   
}

【讨论】:

  • 这似乎奏效了!!太感谢了!!!我一直在尝试使用 File::Find::Rule(尽管我在帖子中提到了 File::Find...)
【解决方案2】:

File::Find 模块自 Perl 5.000 以来一直是标准的 Unix 模块。事实上,从 Perl 3.x 开始,它就是一个标准模块,甚至可能更早。事实上,我在我的 Mac 上安装了 Perl 5.12,但我仍然看到旧的 find.pl 文件位于 @INC 目录之一中。

回到 Perl 5 之前(或者甚至在 Perl 4 之前),你会这样做:

require "find.pl";

而不是

use File::Find;

在您的系统上获取find 命令(find.pl 用于向后兼容)。这就是为什么我很难相信你的系统上没有File::Find。这就像说您的 Windows PC 上没有 dir 命令。

运行命令perl -V。那是大写V。这将打印出@INC 目录列表。看看您是否只能在该列表中列出的那些目录中找到File 目录。在那个目录下应该是一个Find.pm Perl 模块。

这是在我运行 Strawberry Perl 的 PC 上的样子:

@INC:
  C:/perl/perl/site/lib
  C:/perl/perl/vendor/lib
  C:/perl/perl/lib
  . 

在我的 Mac 上,@INC 列表中列出了 10 个目录。

还可以查看您的系统上的 Perl 版本。并且,确保您可以读取@INC 中列出的目录。

如果您的系统上没有File::Find,那么您的 Perl 安装肯定有问题。比起File::Find 本身,我更担心这一点。

还有一件事,看看你是否安装了perldoc 命令。如果这样做,请输入:

$ perldoc File::Find

看看这是否为您提供了有关 File::Find 的任何文档。如果是,则意味着 File::Find 在您的系统上。然后运行:

$ perldoc -l File::Find

这将为您提供 File::Find 模块的位置。

在执行任何其他操作之前,请确认 File::Find 在您的系统上确实不存在,或者您在执行其他操作之前没有对它的读取权限。正如我之前所说,如果你的系统上不存在这个模块,那么你的 Perl 安装就会出现重大问题,我会担心它是否可以信任。这需要解决。

如果一切正常,那么我们需要查看您的程序以找出您不能使用File::Find 的原因。这可能是次要的,例如在程序名称周围使用引号。

【讨论】:

  • 非常感谢您提供的所有信息!我实际上只是个白痴,在我的帖子中提到 File::find 时,我一直在尝试让 File::Find::Rule 工作。以前的海报使用 File::Find 发布了一个脚本,它确实有效。尽管我很愚蠢,但您提供的这些信息都是非常有用的,我再次感谢您:)
  • File::Find::Rule 有点不同。我对那个模块不感兴趣。你最终会得到这么多不同的规则,自己编程会更容易。 File::Find::Object 更好。你可以看看我的File::OFind。您可以将Find 目录树(在lib 下)安装在与您的程序相同的目录中。如果不出意外,看看代码。
【解决方案3】:

您的程序存在许多问题。主要错误是您使用了太多全局变量并试图手动使它们与您当前正在处理的目录保持同步。

这是一个列表

  • 总是 use strictuse warnings 用于您编写的每个程序

  • warnings 会告诉你应该写 opendir(DIR, $_[0]) 而不是 opendir(DIR, @_[0])

  • 您将在目录中的每个条目之后将$directory 设置为$previousDir。但是$previousDir只有在当前条目是另一个目录时才被设置,所以在普通文件之后即使没有保存也恢复了值。

  • 您对是否应该读取由全局变量$directory 指定的目录或传递给子程序的参数感到困惑。

到目前为止,最简单的方法是使用子例程参数来指定当前目录而忘记全局变量。这是一个程序,可以满足您的预期

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`;
    }
  }
}

【讨论】:

  • 感谢您实际回答我的问题。尽管我现在知道 File::Find 有效,但我真的很想知道如何手动操作。你的也有效!! :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-06
  • 1970-01-01
相关资源
最近更新 更多