3

我是一个 Perl 新手。我正在尝试删除目录(A)及其所有子目录(B,C)中具有特定扩展名的所有文件。我已经学会了如何为给定的目录这样做,但不是递归的。即以下在 A 目录中执行工作,但不在 B、C 子目录中。

use strict;    
use warnings;    
my $dir = "~/A/";    
unlink glob "$dir/*.log";

我试过了

use strict;
use warnings;
use File::Find;
my $dir = "~/A";
find(\&wanted, $dir);
sub wanted { 
unlink glob "*.log";
}

但后来我收到一条消息:Can't stat ~/A: No such file or directory。虽然目录在那里。有什么提示吗?马里奥

4

7 回答 7

5

尝试使用$ENV{"HOME"}而不是~特定于外壳的,

use strict;    
use warnings;    
my $dir = "$ENV{HOME}/A";
unlink glob "$dir/*.log";
于 2013-05-27T10:42:28.343 回答
4

在您的第二个脚本中,在find函数内部不要进行其他搜索,因为该函数已经使用递归遍历了树。只需比较文件是否为日志并将其删除。单线:

perl -MFile::Find -e '
    find( 
        sub { m/\.log$/ and do { unlink $_ or warn qq|Could not unlink file _$\n| } 
        }, 
        shift 
    )
' .

它接受一个参数,.在我的例子中是在当前目录开始搜索。

于 2013-05-27T10:51:53.170 回答
1

你是对的,glob没有递归到子目录。

我将运行以下代码as-is,以便您可以看到它在做什么。一旦你明白了,你可以$DEBUG关闭或从代码中删除它。

#!/usr/bin/perl

use warnings;
use strict;
use File::Find;

my $path = "$ENV{HOME}/A";
my $DEBUG = 1;

find(\&wanted, $path);

sub wanted {
    return if ! -e; 

    my $file = $File::Find::name;

    if ($DEBUG) {
        if( $file =~ /\.log$/ ) { 
            print "Log file found: $file\n"
        } else {
            print "Non-log file found: $file\n";
        }   
    } else {
        # anything that ends with '.log'
        unlink $file if $file =~ /\.log$/;
    }   
}
于 2013-05-27T16:16:13.133 回答
1

当我尝试用 /root/ 替换它时,Find::File 似乎与“~”标记有问题,它工作正常:所以@mpapec 建议将其更改为 $ENV{HOME}

use strict;
use warnings;
use File::Find;
my $dir = "$ENV{HOME}/A";
find(\&wanted, $dir);
sub wanted {
unlink glob "*.log";
}
于 2013-05-27T11:05:13.357 回答
1

glob如果您已经在使用 find ,我不会打扰。不妨简单地找到您想要的文件并删除它们:

use strict;
use warnings;
use File::Find;
use Env qw(HOME);

use constant {
    SUFFIX_LIST => qr/\.(log|foo|bar)$/,
    DIR_TO_CHECK => $HOME,
};

@file_list;

find ( sub {
    return unless -f;
    return unless $_ ~= SUFFIX_LIST;
    push @file_list, $File::Find::name;
}, DIR_TO_CHECK );

unlink @file_list;

我已经定义了一个正则表达式(即qr/.../),它定义了我感兴趣的后缀列表。我将常量设置SUFFIX_LIST为这个正则表达式。如果我的文件名与我的正则表达式匹配,那么它就是我要删除的文件。

我定义一个@file_list我做的主要是出于习惯和工作方式find。我不是一个大find粉丝,但这就是我们所拥有的。问题是find希望您的所有代码都包含在find子例程中,这是一种不好的做法。为了解决这个问题,我find将我想要的子程序推送到一个数组中,然后对该数组进行操作。

在这个特定的程序中,我本可以做unlink的,find因为它太短了。然而,大多数时候,你最好使用这种技术。

find函数使用两个特殊的包变量和。第一个是带有完整路径的文件的名称,以提供给命令的目录名称开头。第二个是目录的名称(完整路径)。该函数还设置为当前文件名。因为实际上是在文件所在的目录中,上面没有目录名,可以用来测试文件。$File::Find::name$file::Find::dirfindfind$_find$_

我做了两个测试:1)。这是一个文件吗?和2)。此文件的名称是否以我感兴趣的后缀之一结尾。(请注意,第一个,我可以简单地使用unless -f,而第二个,我必须指定$_变量。)。

如果该文件是一个文件并且具有正确的后缀,我将它推送到我的@file_list数组中。

我更喜欢将我想要的子程序嵌入到我的find命令中。它将函数与影响它的代码保持在一起。以下两个是等价的:

find ( sub {
    return unless -f;
    return unless $_ ~= SUFFIX_LIST;
    push @file_list, $File::Find::name;
}, DIR_TO_CHECK );

find (\&wanted, DIR_TO_CHECK );

sub wanted {
    return unless -f;
    return unless $_ ~= SUFFIX_LIST;
    push @file_list, $File::Find::name;
};

我将常量用于真正是常量的事物。这是一个很好的编程习惯。Perl 常量有点古怪,因为它们没有标记。因此,当您在可能与字符串混淆的情况下使用它们时,您必须小心。

我还使用use Env拉入我想要定义的环境变量,并且只有那些。我可以通过$ENV{HOME}构造将它们拉进来。这取决于您的喜好。该$ENV{..}构造清楚地表明您正在引入环境变量。use Env看起来更干净。

于 2013-05-27T17:01:53.260 回答
0

你在 Linux 上运行吗?如果是这样,我有一个替代解决方案可能会有所帮助。我的基础是在没有说明所需语言的情况下,问题是“我需要删除所有具有特定扩展名的文件,并以递归方式执行”。如果这是更大工作的一部分,请忽略我的回答,如果您只是在做一些管理,它可能会起作用:

find . -type f -name "*.ext" -exec rm {} \;

这将找到当前目录及以下目录中的所有文件,然后将它们的路径传递给 rm 命令。

于 2013-05-27T10:52:29.803 回答
0

您可以使用 opendir / readdir。这是我的解决方案,用于管理具有不同保留期的多个目录,并可选择指定带或不带正则表达式的文件

#Add directories to be maintained "|" delimited days to keep files.
my @directories_and_retention = (
qq!$ENV{ARCDIR}|3|\\.lok\$!, #be careful
qq!$ENV{APPPATH}/ldap/logs|5!,
qq!$ENV{LOGDIR}/canary|2!,
qq!$ENV{LOGDIR}/metadata|30!,
qq!$ENV{LOGDIR}/archive|45!
);

foreach my $directory (@directories_and_retention) {
        my ($path,$retention_days,$file) = split(/\|/,$directory);

        opendir (DIR, "$path");
        my @logfiles = readdir(DIR);
        closedir (DIR);

        foreach $logfile (@logfiles) {
                next if ($logfile =~ /^\.\./);
                next if ($logfile =~ /^\./);
                next if (-d "$path/$logfile");

                if ($file) {
                        next unless ($logfile =~ /$file/);
                }

                if (-M "$path/$logfile" > $retention_days) {
                        print "$path/$logfile > $retention_days\n";
                        unlink("$path/$logfile");
                }
        }
}
于 2014-12-18T14:58:55.740 回答