2013-05-27 70 views
3

我是一个Perl新手。我试图删除目录(A)及其所有子目录(B,C)中具有特定扩展名的所有文件。我已经学会了如何为给定的目录做到这一点,但不是递归的。以下是A目录中的作业,而不是B,C子目录中的作业。使用Perl删除目录和子目录中给定扩展名的文件

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

[为什么我的Perl脚本失败的“〜/”,而是以“$ ENV {HOME}”的作品?] (http://stackoverflow.com/questions/976968/why-does-my-perl-script-fail-on-but-works-with-envhome) – devnull

回答

5

尝试$ENV{"HOME"},而不是~是外壳具体,

use strict;  
use warnings;  
my $dir = "$ENV{HOME}/A"; 
unlink glob "$dir/*.log"; 
+0

辉煌!非常感谢。 – mariodrumblue

+1

@mariodrumblue为什么这被标记为答案?这并没有解决'在B,C子目录中取消链接'日志文件的问题。' – chrsblck

+0

@chrsblck。它实际上解决了在子目录B和C中取消链接的问题。至少对我而言。这就是为什么我标记为答案。 – mariodrumblue

4

在你的第二个脚本,该find函数内不要做另一次搜索,因为该功能已遍历使用递归的树。只需比较文件是否为日志并将其删除即可。一行:

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

它接受一个参数,.在我的情况下开始在当前目录搜索。

+0

谢谢你,因为我今天开始学习Perl,所以我有点麻烦来完全理解你的脚本,但是我会在接下来的日子里去试试。 – mariodrumblue

+2

我喜欢这个回答。其他一些答案和原始问题对File :: find的作用和glob做了什么感到困惑。 File :: find访问起始目录中的所有文件和目录,因此不需要使用glob。作为一个实验,我建议@mariodrumblue(和其他人)尝试原始脚本,但替换'unlink glob“* .log”;'用'print“取消关联glob * .log for $ File :: Find :: name \ n” '。 – AdrianHHH

+0

@AdrianHHH。谢谢。替换为“取消链接$ File :: Find :: name \ n的glob * .log”让我明白发生了什么。在查看目录时,Glob非常棒,但是File :: find会一直到达文件的路径,因此不再需要glob。使用匹配运算符m /就足够了。 – mariodrumblue

0

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

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

这会发现所有的文件在当前目录及以下,然后将他们的路径传递给rm命令。

+0

不,我在Mac OS X中工作。我一直在编写基本的shell脚本,但现在我想学习Perl。无论如何感谢提示! – mariodrumblue

+0

不用担心!希望它能在未来派上用场。 – chooban

1

看来,Find :: File出现“〜”标记问题,当我尝试用例如/ root /替换它时,它工作正常: 因此,@mpapec已将其更改为$ ENV {HOME}

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

这是一个糟糕的例子:'glob'和'File :: Find'的组合。 – chrsblck

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$/; 
    } 
} 
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函数使用两个特殊的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看起来更清洁。

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"); 
       } 
     } 
} 
相关问题