Perl实现目录-文件哈希数组时无法识别子目录的问题求助
问题描述
我想要实现这样的功能:遍历当前目录下的所有子目录,为每个目录创建一个数组存储该目录及其所有子目录下的文件,最后把这些数组存入一个哈希(键是目录名,值是对应文件数组)。我写了一段Perl代码,虽然能列出目录里的文件,但无法识别子目录——明明有些目录里有子目录,代码里判断-d $file的语句却从来不会输出。我也试过用glob处理子目录,但效果不理想,麻烦大家帮我看看代码哪里出问题了?
附上我写的代码:
#!/usr/bin/perl -w use strict; use warnings; use Data::Dumper qw(Dumper); use File::Basename; my %hash; my @store; my @list_of_dirs = grep { -d } glob "*"; print "LIST of DIRs = @list_of_dirs \n"; foreach my $dir (@list_of_dirs) { opendir(DIR, $dir) or die $!; while (my $file = readdir(DIR)) { next if ($file =~ m/^\./); print "FILE = $file\n"; if ( -d $file) { print "IT IS A DIRECTORY\n"; } ## 这句从来不会打印,哪怕目录里确实有子目录 push(@store, $file); } #end_of_while print "$dir = @store \n"; $hash{"$dir"} = [@store]; @store = (); } #end_of_foreach print Dumper \%hash;
我还试过这段处理子目录的代码,但也没达到预期:
while ( my $subdir = glob "$file/*" ) { my @sub_files = grep -f, glob "$subdir/*"; print "SUBDIR $dir = @sub_files\n"; }
问题根源
你代码里判断子目录的逻辑有个关键错误:当你用readdir读取$dir目录下的内容时,得到的$file只是文件名/子目录名,不是完整路径。这时候用-d $file判断的是当前工作目录下是否存在这个目录,而不是$dir目录下的子目录,自然判断失败了。
举个例子:假设你当前目录有个docs目录,里面有个images子目录。当遍历docs时,readdir得到的$file是images,这时候-d images检查的是当前目录下有没有images,而不是docs/images,所以返回false,不会打印那句提示。
修正方案
我们需要把$dir和$file拼接成完整路径,再进行文件类型判断。另外,为了递归遍历所有子目录里的文件,最好写一个递归函数来处理,这样不管嵌套多少层子目录都能覆盖到。
下面是修正后的完整代码:
#!/usr/bin/perl use strict; use warnings; use Data::Dumper qw(Dumper); use File::Spec; # 用来安全拼接路径,跨平台兼容 my %dir_files_hash; # 获取当前目录下的所有子目录 my @list_of_dirs = grep { -d } glob "*"; # 遍历每个目录,收集所有文件(包括子目录里的) foreach my $dir (@list_of_dirs) { my @all_files = (); collect_files($dir, \@all_files); $dir_files_hash{$dir} = \@all_files; print "$dir 下的文件列表:@all_files\n"; } print "\n最终的哈希结构:\n", Dumper(\%dir_files_hash); # 递归收集目录下的所有文件 sub collect_files { my ($current_dir, $files_ref) = @_; opendir(my $dh, $current_dir) or die "无法打开目录 $current_dir: $!"; while (my $entry = readdir($dh)) { next if $entry =~ /^\./; # 跳过隐藏文件和.、.. # 拼接完整路径 my $full_path = File::Spec->catfile($current_dir, $entry); if (-d $full_path) { # 如果是子目录,递归遍历它 collect_files($full_path, $files_ref); } else { # 如果是文件,添加到数组里 push @$files_ref, $entry; # 如果你需要完整路径,就push $full_path } } closedir($dh); }
关键修改点说明
- 使用完整路径判断:用
File::Spec->catfile拼接目录和文件名,确保路径跨平台兼容,判断-d $full_path就能正确识别子目录了。 - 递归函数遍历子目录:写了
collect_files递归函数,遇到子目录就深入进去,把里面的文件也收集到当前目录的数组里,完全满足你的需求。 - 代码更规范:用
my $dh这种词法句柄代替全局的DIR,避免潜在的句柄冲突;去掉了不必要的-w(因为已经用use warnings了)。
如果你需要在数组里存储完整文件路径而不是只存文件名,只需要把push @$files_ref, $entry;改成push @$files_ref, $full_path;就行。
备注:内容来源于stack exchange,提问作者nandeesh v

