1

我正在尝试使用 File::Find 来 1) 遍历给定的文件夹和子文件夹,删除任何超过 30 天的文件,以及 b) 如果父文件夹在所有删除后为空,也将其删除。

这是我的代码:

use strict;
use warnings;
no warnings 'uninitialized';
use File::Find;
use File::Basename;
use File::Spec::Functions;

# excluding some home brew imports


# go into given folder, delete anything older than 30 days, and if folder is then empty,     delete it

my $testdir = 'C:/jason/temp/test';
$testdir =~ s#\\#/#g;

open(LOG, ">c:/jason/temp/delete.log");

finddepth({ wanted => \&myWanted, postprocess => \&cleanupDir }, $testdir);

sub myWanted {

   if ($_ !~ m/\.pdf$/i &&
       int(-M $_) > 30
      ) 
   {
      my $age = int(-M $_);
      my $path = $File::Find::name;
      print LOG "age : $age days - $path\n";
      unlink($path);

   }
}


sub cleanupDir {
   my $path = $File::Find::dir;
   if ( &folderIsEmpty($path) ) {
      print LOG "deleting : $path\n";
      unlink($path);
   } else {
      print LOG "$path not empty\n";
      my @files = glob("$path/*");
      foreach my $file(@files){
         print LOG "\t$file\n";
      }
   }

}

我原以为 finddepth() 会走到树的底部并向上工作,但那没有发生。该脚本在某些电子书内容的解压缩上运行,即使所有文件都已删除,也不会删除具有子文件夹的目录。

age : 54 days - C:/jason/temp/test/mimetype
age : 54 days - C:/jason/temp/test/META-INF/container.xml
age : 54 days - C:/jason/temp/test/META-INF/ncx.xml.kindle
deleting : C:/jason/temp/test/META-INF
age : 54 days - C:/jason/temp/test/OEBPS/content.opf
age : 54 days - C:/jason/temp/test/OEBPS/cover.html
age : 54 days - C:/jason/temp/test/OEBPS/ncx.xml
age : 54 days - C:/jason/temp/test/OEBPS/pagemap.xml
age : 54 days - C:/jason/temp/test/OEBPS/t01_00_text.html
age : 54 days - C:/jason/temp/test/OEBPS/t02_00_text.html
age : 54 days - C:/jason/temp/test/OEBPS/t03_00_text.html
age : 54 days - C:/jason/temp/test/OEBPS/t04_00_text.html
age : 54 days - C:/jason/temp/test/OEBPS/t05_00_text.html
age : 54 days - C:/jason/temp/test/OEBPS/t06_00_text.html
age : 54 days - C:/jason/temp/test/OEBPS/t07_00_text.html
age : 54 days - C:/jason/temp/test/OEBPS/t08_00_text.html
age : 54 days - C:/jason/temp/test/OEBPS/t08_01_text.html
age : 54 days - C:/jason/temp/test/OEBPS/media/cover.jpg
age : 54 days - C:/jason/temp/test/OEBPS/media/flamlogo.gif
age : 54 days - C:/jason/temp/test/OEBPS/media/logolnmb.jpg
age : 54 days - C:/jason/temp/test/OEBPS/media/stylesheet.css
deleting : C:/jason/temp/test/OEBPS/media
C:/jason/temp/test/OEBPS not empty
    C:/jason/temp/test/OEBPS/media
C:/jason/temp/test not empty
    C:/jason/temp/test/META-INF
    C:/jason/temp/test/OEBPS

看起来 C:/jason/temp/test/OEBPS/media/ 已删除,但在调用预处理函数时未注册该删除。关于如何让它发挥作用的任何想法?谢谢!

谢谢,bp

4

3 回答 3

3

正如Miller评论的那样,您不能unlink创建目录。此外,File::Findchdir调用wanted. 这意味着,在postprocess子例程中,您正试图删除您当前的工作目录。Windows 不会喜欢这样的。

我会这样写。我已经对其进行了测试,但是您显然应该非常小心删除磁盘存储内容的任何内容。

use strict;
use warnings;
use autodie;

use File::Find;
use File::Spec::Functions;

my $testdir = 'C:\jason\temp\test';

open my $log, '>', 'C:\jason\temp\delete.log';

finddepth(\&wanted, $testdir);

sub wanted {

  my $full_name = canonpath $File::Find::name;

  if (-f) {
    my $age  = int(-M);
    unless ( /\.pdf\z/ or $age <= 30) {
      print $log "Age: $age days - $full_name\n";
      unlink;
    }
  }
  elsif (-d) {
    my @contents = do {
      opendir my ($dh), $_;
      grep { not /\A\.\.?\z/ } readdir $dh;
    };
    rmdir unless @contents;
  }
}
于 2014-03-12T21:05:37.493 回答
1

我怀疑您实际上并没有删除目录。从文档unlink

注意:除非您是超级用户并且该标志已提供给 Perl unlink,否则不会尝试删除目录。-U即使满足这些条件,请注意取消链接目录可能会对文件系统造成损坏。最后,unlink许多操作系统不支持在目录上使用。改为使用rmdir

于 2014-03-12T19:36:42.093 回答
0

我从不喜欢File::Find,因为它只是一团糟。它吞噬了你的整个程序,因为它希望一切都在你想要的子程序中。另外,我不喜欢我的一半代码散落在各处的事实。但是,每个 Perl 安装都标配了哪些其他工具。我得凑合了。

我更喜欢把我所有的文件都扔进一个数组中。它使代码保持干净。我find刚刚发现。我在其他地方进行其余的处理。我还将我想要的子程序嵌入到我的 find 命令中。它将所有内容保存在一个地方。

此外,您不能用于unlink删除目录。remove_treeFile::Path使用。那是一个标准模块。您还可以使用readdir来查看目录有多少子目录。这是检查它是否为空的好方法:

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

use File::Find;
use File::Path qw(make_path remove_tree);

my $testdir     = 'C:/jason/temp/test';
my $mdate_limit = 30;

my @files;              # We'll store the files here
my %dirs;               # And we'll track the directories that my be empty

#
# First find the files
#
find ( sub {
    return unless -f;                  # We want just files.
    return if -M < $mdate_limit;       # Skip if we've modified since $mdate_limit days
    push @files, $File::Find::name;    # We're interested in this file,
    $dirs{$File::Find::dir} = 1;       # and the directory that file is in
}, $testdir );

#
# Delete the files that you've found
#

unlink @files;

#
# Go through the directories and see which are empty
#

for my $dir ( sort keys %dirs ) {
    opendir my $dir_fh, $dir or next;  # We'll skip bad reads
    my @dir_files = readdir $dir_fh;
    close $dir_fh;
    if ( @dir_files <= 2 ) {   # Directory is empty if there's only "." and ".." in it
        remove_tree( $dir )
          or warn qq(Can't remove directory "$dir"\n);
    }
}

请注意,我已经嵌入了我的wanted例程:

find ( sub {
    return unless -d;                  # We want just files.
    return if -M < $mdate_limit;       # File hast been modified in the $mdate_limit days
    push @files, $Find::File::name;    # We're interested in this file
    $dirs{$Find::File::dir} = 1;       # The directory that file is in
}, $testdir );

替代方案是这样的:

file (\&wanted, $testdir);

sub wanted {
    return unless -d;                  # Okay...
    return if -M < $mdate_limit;       # Um... Where's $mdate_limit defined?
    push @files, $Find::File::name;    # And @files?
    $dirs{$Find::File::dir} = 1;       # And %dirs?
}

问题是我的wanted子程序包含三个全局变量。而且,我的命令有可能与我的子程序find分开。wanted在 3 个月的时间内,您将不得不搜索整个代码以找到该wanted例程。

而且,当您确实看到该wanted子程序时,就会发现这三个神秘的全局变量。它们在哪里定义?那是一个错误吗?

通过将子例程与我的 find 相结合,我保证find命令所需的子例程不会偏离我的find. 另外,它隐藏了嵌入在我的子程序中的这三个变量的全局性。

没有什么能阻止我删除find 命令中的文件。在搜索时更改目录结构通常不是一个好主意,但这应该没问题。

但是,我喜欢我的find命令只是找到我感兴趣的文件。我不希望我的程序的 1/2 塞在那里。它成为维护的噩梦。我会忍受一些低效率的。加载包含一百万个文件的数组可能需要整整@files一两秒,但一旦我必须调试我的程序,我将花费比这更长的时间。

于 2014-03-12T22:30:13.807 回答