【问题标题】:How to get the list of files that are not in another directory in perl如何获取不在perl中另一个目录中的文件列表
【发布时间】:2016-08-26 16:31:02
【问题描述】:

我必须修复一个 Perl 脚本,该脚本执行以下操作:

# Get the list of files in the staging directory; skip all beginning with '.'
opendir ERR_STAGING_DIR, "$ERR_STAGING" or die "$PID: Cannot open directory $ERR_STAGING";
@allfiles = grep !/^$ERR_STAGING\/\./, map "$ERR_STAGING/$_", readdir(ERR_STAGING_DIR);
closedir(ERR_STAGING_DIR);

我有两个目录,一个是STAGING,另一个是ERROR。 STAGING 包含 ABC_201608100000.fin 之类的文件,ERR_STAGING_DIR 包含 ABC_201608100000.fin.bc_lerr.xml。现在 Perl 脚本作为守护进程运行,它不断在ERR_STAGING_DIR 目录中查找文件并处理错误文件。

但是,如果ABC_201608100000.fin 存在于 STAGING 中,我的要求是不要处理该文件。

问题:

有没有办法,我可以过滤allfiles数组并选择STAGING目录中不存在的文件?

我尝试了什么:

我已经通过编程方式忽略了 STAGING 目录中存在的文件。虽然它不起作用。

     # Move file from the staging directory to the processing directory.
        @splitf = split(/.bc_lerr.xml/,basename($file));
        my $finFile = $STAGING . "/" . $splitf[0];
        print LOG "$PID: Staging File $finFile \n";

        foreach $file(@sorted_allfiles) {    
        if ( -e $finFile )
        {
            print LOG "$PID: Staging File still exist.. moving to next $finFile \n";
            next;
        }
        # DO THE PROCESSING.

【问题讨论】:

    标签: perl


    【解决方案1】:

    撇开时间问题不谈,我假设可以处理文件快照,而不必担心出现新文件。我认为@allfiles 具有来自ERROR 目录的所有文件名。

    在每次迭代时从数组的前面删除一个文件名。检查STAGING中的对应文件,如果不存在则处理掉,否则将其推到数组的背面并跳过。

    while (@allfiles) 
    {
         my $errfile = shift @allfiles;
    
         my ($file) = $errfile =~ /(.*)\.bc_lerr\.xml$/;
    
         if (-e "$STAGING/$file")
         {
              push @allfiles, $errfile;
              sleep 1;                    # more time for existing files to clear
              next;
         }
         # process the error file
    }
    

    如果处理速度快于 STAGING 中现有文件消失所需的处理速度,我们将耗尽所有可处理文件,然后继续运行文件测试。没有理由滥用资源,因此sleep,给STAGING 文件更多的时间离开。请注意,如果 STAGING 中只有一个文件未能消失,则此循环将继续检查它,您需要添加一些防范措施。

    另一种方法是使用foreach 处理错误文件,并将那些应该跳过的文件添加到单独的数组中。然后可以单独尝试,可能需要适当的等待。

    这是否合适取决于整个过程的细节。 STAGING 文件会保留多长时间,这是典型的还是异常的?新文件多久出现一次?通常有多少个文件?


    如果您只想过滤掉在STAGING中有对应的错误文件

    my @errfiles_nostaging = grep { 
        my ($file) = $_ =~ /(.*)\.bc_lerr\.xml$/;
        not -e "$STAGING/$file";
    } @allfiles;
    

    输出数组包含来自@allfiles 的文件,这些文件在$STAGING 中没有对应的文件,可以很容易地处理。如果与$STAGING 文件的停留时间相比,错误文件的处理速度非常快,这将是合适的。

    过滤器也可以写在一个语句中。例如

    grep { not -e "$STAGING/" . s/\.bc_lerr\.xml$//r }              # / or
    grep { not -e "$STAGING/" . (split /\.bc_lerr\.xml$/, $_)[0] }
    

    第一个示例使用 非破坏性 /r 修饰符,可用 since 5.14。它将替换更改为 return 已更改的字符串,而 不更改 原始字符串。见in perlrequickin perlop

    【讨论】:

    • @user2570205 这个答案是第一次,因为我不知道你的整个问题的任何相关细节。如果您提供反馈或额外的要求或解释,我可以调整。
    • 有没有办法在 grep 语句中过滤?
    • @user2570205 是 - 添加到答案的末尾。如果您需要一份声明,请告诉我。如果我应该解释它是如何工作的,请告诉我。
    【解决方案2】:

    这是一个非常暴力的例子,但是如果你有一个数组中的暂存目录的内容,你可以在读取错误目录的内容时检查该数组。

    我对文件名的关系做了一些巨大的假设——基本上舞台目录包含被截断的文件,特别是你在示例中列出的方式。如果普遍情况如此,那么子字符串的工作速度会更快,但如果您的示例被简化以说明问题,则此示例更具可扩展性。

    use strict;
    
    my @error = qw(
      ABC_201608100000.fin.bc_lerr.xml
      ABD_201608100000.fin.bc_lerr.xml
      ABE_201608100000.fin.bc_lerr.xml
      ABF_201608100000.fin.bc_lerr.xml
    );
    
    my @staging = qw(
      ABC_201608100000.fin
      ABD_201608100000.fin
    );
    
    foreach my $error (@error) {
      my $stage = $error;
      $stage =~ s/\.bc_lerr\.xml//;
    
      unless (grep { /$stage/ } @staging) {
         ## process the file here
      }
    }
    

    此示例中的grep 为 O(n),因此如果您有一个非常大的任一数组列表,您可能希望首先将其加载到哈希中,这将是 O(1)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-09-17
      • 1970-01-01
      • 1970-01-01
      • 2010-12-22
      • 2020-09-11
      • 2020-06-20
      • 1970-01-01
      • 2012-09-22
      相关资源
      最近更新 更多