0

我开始自学 Perl,在一些谷歌搜索的帮助下,我能够编写一个脚本,打印出给定目录中的文件扩展名。该代码运行良好,但是有时会抱怨以下内容:

Use of uninitialized value $exts[xx] in string eq at get_file_exts.plx

我试图通过如下初始化我的数组来纠正这个问题:my @exts = (); 但这并没有按预期工作。

#!/usr/bin/perl
use strict; 
use warnings;
use File::Find;

#Check for correct number of arguments
if(@ARGV != 1) {
    print "ERROR: Incorrect syntax...\n";
    print "Usage: perl get_file_exts.plx <Directory>\n";
    exit 0;
}

#Search through directory
find({ wanted => \&process_file, no_chdir => 1 }, @ARGV);

my @exts;

sub process_file {
    if (-f $_) {
        #print "File: $_\n";
        #Get extension
        my ($ext) = $_ =~ /(\.[^.]+)$/;

        #Add first extension
        if(scalar @exts == 0) {
            push(@exts, $ext);
        }

        #Loop through array
        foreach my $index (0..$#exts) {
            #Check for match
            if($exts[$index] eq $ext) {
                last;
            } 
            if($index == $#exts) {
                push(@exts, $ext);
            }
        }
    } else {
        #print "Searching $_\n";
    }
}

#Sort array
@exts = sort(@exts);

#Print contents
print ("@exts", "\n");
4

4 回答 4

2

警告是抱怨 的内容$exts[xx],而不是@exts它本身。

实际上$ext可以是undef,当文件名与您的正则表达式不匹配时,例如README

试试看:

    my ($ext) = $_ =~ /(\.[^.]+)$/ or return;
于 2013-04-19T16:47:17.970 回答
2

您需要测试是否找到了扩展。

此外,您不应该索引您的数组。您也不需要管理“推送”,只需执行此操作即可。这不是 Perl 的方式。你的 for 循环应该像这样开始:

sub process_file {
  if (-f $_) {
    #print "File: $_\n";
    #Get extension
    my ($ext) = $_ =~ /(\.[^.]+)$/;

    # If we found an extension, and we have not seen it before, add it to @exts
    if ($ext) {
      #Loop through array to see if this is a new extension
      my $newExt = 1;
      for my $seenExt (@exts) {
        #Check for match
        if ($seenExt eq $ext) {
          $newExt = 0
          last;
        }
      }

      if ($newExt) {
        push @exts,$ext;
      }
    }
  }
}

但是你真正想做的是使用哈希表来记录你是否看到了一个扩展

# Move this before find(...); if you want to initialize it or you will clobber the 
# contents
my %sawExt;

sub process_file {
  if (-f $_) {
    #print "File: $_\n";
    # Get extension
    my ($ext) = $_ =~ /(\.[^.]+)$/;

   # If we have an extension, mark that we've seen it
   $sawExt{$ext} = 1
     if $ext;
  }
}

# Print the extensions we've seen in sorted order
print join(' ',sort keys %sawExt) . "\n";

甚至

sub process_file {
  if (-f $_ && $_ =~ /(\.[^.]+)$/) {
    $sawExt{$1} = 1;
  }
}

或者

sub process_file {
  $sawExt{$1} = 1
    if -f && /(\.[^.]+)$/;
}

一旦你开始在 Perl 中思考,这就是编写它的自然方式

于 2013-04-19T18:44:23.257 回答
1

主要问题是您没有考虑不包含点的文件名,所以

my ($ext) = $_ =~ /(\.[^.]+)$/;

设置$extundef

尽管有警告,处理继续通过评估undef为空字符串,未能在 中找到它@exts,因此也渗透undef到数组中。

使您的代码正常工作的最小更改是替换

my ($ext) = $_ =~ /(\.[^.]+)$/;

return unless /(\.[^.]+)$/;
my $ext = $1;

但是这里有几个 Perl 课程需要学习。曾经有人教导说,好的程序是评论良好的程序那是在不得不编写高效但难以理解的代码的时代,但现在不再如此。您应该编写尽可能清晰的代码,并且仅在您绝对必须编写一些不自我解释的内容时才添加注释。

您应该记住并使用 Perl 习语,并尝试忘记您所知道的大多数 C。例如,Perl 接受“here document”语法,并且通常将orandand用作短路运算符。您的参数检查变为

@ARGV or die <<END;
ERROR: Incorrect syntax...
Usage: perl get_file_exts.plx <Directory>
END

Perl 允许清晰而简洁的编程。这就是我写你的wanted子程序的方式

sub process_file {

  return unless -f and /(\.[^.]+)$/;

  my $ext = $1;

  foreach my $index (0 .. $#exts) {
    return if $exts[$index] eq $ext;
  }

  push @exts, $ext;
}
于 2013-04-19T18:49:58.483 回答
0

exists$exts[xx]访问它之前使用。

exists尽管正如@chrsblck 指出的那样,但已弃用:

请注意,对数组值调用存在已被弃用,并且可能在 Perl 的未来版本中被删除。

但是您应该能够简单地检查它是否存在(而不是0or ""):

if($exts[index] && $exts[$index] eq $ext){
   ...
}
于 2013-04-19T16:43:19.740 回答