我是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
。虽然目录在那里。任何提示?
马里奥
答案 0 :(得分:5)
尝试使用$ENV{"HOME"}
代替~
,这是特定于shell的,
use strict;
use warnings;
my $dir = "$ENV{HOME}/A";
unlink glob "$dir/*.log";
答案 1 :(得分:4)
在第二个脚本中,find
函数内部不执行其他搜索,因为该函数已使用递归遍历树。只需比较文件是否为日志并将其删除即可。单行:
perl -MFile::Find -e '
find(
sub { m/\.log$/ and do { unlink $_ or warn qq|Could not unlink file _$\n| }
},
shift
)
' .
它接受一个参数,.
在我的情况下开始在当前目录搜索。
答案 2 :(得分:1)
当我尝试用例如/ root /替换它时,似乎Find :: File有“〜”标记的问题,它可以正常工作: 因此@mpapec sugested将其更改为$ ENV {HOME}
use strict;
use warnings;
use File::Find;
my $dir = "$ENV{HOME}/A";
find(\&wanted, $dir);
sub wanted {
unlink glob "*.log";
}
答案 3 :(得分: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$/;
}
}
答案 4 :(得分:1)
如果你已经使用了find,我不会打扰glob
。也可以简单地找到你想要的文件并删除它们:
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
函数使用两个特殊package variables,$File::Find::name
和$file::Find::dir
。第一个是文件名,上面有完整路径,以find
命令的目录名开头。第二个是目录的名称(完整路径)。 find
函数还将$_
设置为当前文件名。由于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
看起来更干净。
答案 5 :(得分:0)
你在Linux上运行吗?如果是这样,我有一个可能有帮助的替代解决方案。我的基础是,如果不说明所需的语言,问题是“我需要删除具有特定扩展名的所有文件,并以递归方式执行”。如果这是大部分工作的一部分,请忽略我的答案,如果您只是做一些管理员,它可能会有效:
find . -type f -name "*.ext" -exec rm {} \;
这将找到当前目录及下面的所有文件,然后将其路径传递给rm命令。
答案 6 :(得分: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");
}
}
}