1

我写了一些脚本,递归打印目录的内容。但它会为每个文件夹打印警告。如何解决这个问题?

样本文件夹:

dev# cd /tmp/test
dev# ls -p -R
test2/
testfile
testfile2

./test2:
testfile3
testfile4

我的代码:

#!/usr/bin/perl

use strict;
use warnings;

browseDir('/tmp/test');

sub browseDir {
    my $path = shift;
    opendir(my $dir, $path);
    while (readdir($dir)) {
        next if /^\.{1,2}$/;
        if (-d "$path/$_") {
            browseDir("$path/$_");
        }
        print "$path/$_\n";
    }
    closedir($dir);
}

和输出:

dev# perl /tmp/cotest.pl
/tmp/test/test2/testfile3
/tmp/test/test2/testfile4
在 /tmp/cotest.pl 第 16 行的连接 (.) 或字符串中使用未初始化的值 $_。
/tmp /test/
/tmp/test/testfile
/tmp/test/testfile2

4

3 回答 3

1

你可以试试那个代码:

    #!/usr/bin/perl

    use strict;
    use warnings;

    browseDir('/tmp');

    sub browseDir {
        my $path = shift;
        opendir(my $dir, $path);
        while (readdir($dir)) {
            next if /^\.{1,2}$/;
            print "$path/$_\n";
            if (-d "$path/$_") {
                browseDir("$path/$_");
            }
        }
        closedir($dir);
    }

如果你得到那个错误,那是因为你在使用变量 $_ 之前调用了 browseDir()。

于 2013-04-05T12:51:09.323 回答
1

$_您在调用之前放置一个值,browseDir并且您希望它在调用之后存在该值browseDir(一个合理的期望),但是browseDir修改了该变量。

只需添加local $_;browseDir确保在子退出之前撤消对它的任何更改。


与您的问题无关,以下是其他三个问题:

  • 甚至没有最小的错误检查!
  • 您可能会用完目录句柄来导航深层目录。
  • 您过滤掉文件".\n""..\n".

使固定:

#!/usr/bin/perl

use strict;
use warnings;

browseDir('/tmp/test');

sub browseDir {
    my $path = shift;

    opendir(my $dh, $path) or die $!;
    my @files = readdir($dh);
    closedir($dh);

    for (@files) {
        next if /^\.{1,2}z/;
        if (-d "$path/$_") {
            browseDir("$path/$_");
        }

        print "$path/$_\n";
    }
}

最后,为什么不使用像File::Find::Rule这样的模块呢?

use File::Find::Rule qw( );
print "$_\n" for File::Find::Rule->in('/tmp');

注意:在 5.12 之前,while (readir($dh))必须写成while (defined($_ = readdir($dh)))

于 2013-04-05T13:26:27.717 回答
1

为什么不使用File::Find模块?自 Perl 5.x 以来,它几乎包含在所有 Perl 发行版中。它不是我最喜欢的模块,因为它的工作方式有点混乱,但它做得很好。

你定义一个wanted子程序来做你想做的事情并过滤掉你不想要的东西。在这种情况下,您几乎打印了所有内容,因此wanted所做的只是打印找到的内容。

File::Find中,文件名保存在 中$File::Find::name,该文件的目录在 中$File::Find::dir。是$_文件本身,可用于测试。

这是您想要的基本方法:

use strict;
use warnings;
use feature qw(say);

use File::Find;

my $directory = `/tmp/test`;

find ( \&wanted, $directory );

sub wanted {
    say $File::Find::Name;
}

我更喜欢把我的wanted函数放在我的find子程序中,这样它们就在一起了。这相当于上面的:

use strict;
use warnings;
use feature qw(say);

use File::Find;

my $directory = `/tmp/test`;

find ( 
    sub { 
        say $File::Find::Name
    },
    $directory,
);

好的编程说不要在子程序中打印。相反,您应该使用子例程来存储和返回您的数据。不幸的是,find根本不返回任何东西。您必须使用全局数组来捕获文件列表,然后将它们打印出来:

use strict;
use warnings;
use feature qw(say);

use File::Find;

my $directory = `/tmp/test`;
my @directory_list;

find ( 
    sub { 
        push @directory_list, $File::Find::Name
    }, $directory );

for my $file (@directory_list) {
   say $file;
}

或者,如果您更喜欢单独的wanted子例程:

use strict;
use warnings;
use feature qw(say);

use File::Find;

my $directory = `/tmp/test`;
my @directory_list;

find ( \&wanted, $directory );
sub wanted {
    push @directory_list, $File::Find::Name;
}

for my $file (@directory_list) {
   say $file;
}

我想要的子例程依赖于一个不是子例程本地的数组这一事实困扰着我,这就是为什么我更喜欢将wanted子例程嵌入到我的find调用中。

您可以做的一件事是使用您的子例程过滤掉您想要的内容。假设您只对 JPG 文件感兴趣:

use strict;
use warnings;
use feature qw(say);

use File::Find;

my $directory = `/tmp/test`;
my @directory_list;

find ( \&wanted, $directory );
sub wanted {
    next unless /\.jpg$/i;  #Skip everything that doesn't have .jpg suffix
    push @directory_list, $File::Find::Name;
}

for my $file (@directory_list) {
   say $file;
}

请注意,在我将它推入我的数组之前,想要的子例程如何对next我不想要的任何文件执行 a。@directory_list同样,我更喜欢嵌入:

find (sub {
    next unless /\.jpg$/i;  #Skip everything that doesn't have .jpg suffix
    push @directory_list, $File::Find::Name;
}

我知道这不是您所问的,但我只是想让您了解该Find::File模块并向您介绍 Perl 模块(如果您还不了解它们),它可以为 Perl 添加很多功能。

于 2013-04-05T16:20:36.713 回答