目录操作 #

一、目录遍历 #

1.1 opendir/readdir/closedir #

perl
opendir my $dh, "." or die "Cannot open directory: $!";

while (my $entry = readdir $dh) {
    next if $entry eq "." or $entry eq "..";
    print $entry . "\n";
}

closedir $dh;

1.2 获取所有条目 #

perl
opendir my $dh, "." or die $!;
my @entries = readdir $dh;
closedir $dh;

print "$_\n" foreach @entries;

1.3 过滤文件类型 #

perl
opendir my $dh, "." or die $!;

my @dirs = grep { -d $_ } readdir $dh;
rewinddir $dh;
my @files = grep { -f $_ } readdir $dh;

closedir $dh;

print "Directories: @dirs\n";
print "Files: @files\n";

1.4 使用glob #

perl
my @txt_files = glob "*.txt";
my @all_files = glob "*";
my @perl_files = glob "*.pl *.pm";

print "$_\n" foreach @txt_files;

二、目录创建与删除 #

2.1 创建目录 #

perl
mkdir "newdir" or die "Cannot create: $!";

mkdir "newdir", 0755 or die "Cannot create: $!";

2.2 创建多级目录 #

perl
use File::Path qw(make_path);

make_path "a/b/c/d" or die "Cannot create: $!";

2.3 删除目录 #

perl
rmdir "emptydir" or die "Cannot remove: $!";

2.4 删除非空目录 #

perl
use File::Path qw(remove_tree);

remove_tree "dir" or die "Cannot remove: $!";

三、路径操作 #

3.1 当前目录 #

perl
use Cwd;

my $cwd = getcwd();
print "Current directory: $cwd\n";

3.2 切换目录 #

perl
chdir "/tmp" or die "Cannot change: $!";

{
    local $ENV{PWD} = "/tmp";
}

3.3 路径分解 #

perl
use File::Basename;

my $path = "/path/to/file.txt";

my $dirname = dirname($path);
my $basename = basename($path);
my ($name, $dirs, $suffix) = fileparse($path, qr/\.[^.]*/);

print "Dirname: $dirname\n";
print "Basename: $basename\n";
print "Name: $name, Dirs: $dirs, Suffix: $suffix\n";

3.4 路径拼接 #

perl
use File::Spec;

my $path = File::Spec->catfile("path", "to", "file.txt");
print $path;

my $abs_path = File::Spec->rel2abs("file.txt");
print $abs_path;

四、File::Find模块 #

4.1 基本遍历 #

perl
use File::Find;

find(sub {
    print $File::Find::name . "\n";
}, ".");

4.2 查找特定文件 #

perl
use File::Find;

my @perl_files;

find(sub {
    push @perl_files, $File::Find::name if /\.pl$/;
}, ".");

print "$_\n" foreach @perl_files;

4.3 查找目录 #

perl
use File::Find;

my @dirs;

find(sub {
    push @dirs, $File::Find::name if -d $_;
}, ".");

print "$_\n" foreach @dirs;

4.4 预处理 #

perl
use File::Find;

find({ 
    wanted => sub {
        print $File::Find::name . "\n";
    },
    preprocess => sub {
        return sort @_; 
    },
}, ".");

五、Path::Tiny模块 #

5.1 创建路径对象 #

perl
use Path::Tiny;

my $path = path("/path/to/file.txt");
my $dir = path("/path/to/dir");

5.2 文件操作 #

perl
use Path::Tiny;

my $file = path("test.txt");

my $content = $file->slurp;
my @lines = $file->lines;

$file->spew("New content\n");
$file->append("More content\n");

5.3 目录操作 #

perl
use Path::Tiny;

my $dir = path(".");

my @children = $dir->children;
my @files = $dir->children(qr/\.txt$/);

$dir->child("subdir")->mkpath;

5.4 路径操作 #

perl
use Path::Tiny;

my $path = path("/path/to/file.txt");

my $parent = $path->parent;
my $basename = $path->basename;
my $absolute = $path->absolute;
my $relative = $path->relative;

print "Parent: $parent\n";
print "Basename: $basename\n";

六、实用示例 #

6.1 递归文件大小统计 #

perl
use File::Find;

my $total_size = 0;

find(sub {
    $total_size += -s $_ if -f $_;
}, ".");

print "Total size: $total_size bytes\n";

6.2 查找重复文件 #

perl
use strict;
use warnings;
use File::Find;
use Digest::MD5;

my %files;

find(sub {
    return unless -f $_;
    
    open my $fh, "<", $_ or return;
    my $md5 = Digest::MD5->new;
    $md5->addfile($fh);
    my $hash = $md5->hexdigest;
    close $fh;
    
    push @{$files{$hash}}, $File::Find::name;
}, ".");

foreach my $hash (keys %files) {
    if (@{$files{$hash}} > 1) {
        print "Duplicates:\n";
        print "  $_\n" foreach @{$files{$hash}};
    }
}

6.3 清理临时文件 #

perl
use strict;
use warnings;
use File::Find;

my @to_delete;

find(sub {
    if (/\.tmp$/i || /\.bak$/i) {
        push @to_delete, $File::Find::name;
    }
}, ".");

foreach my $file (@to_delete) {
    print "Delete $file? [y/N] ";
    my $answer = <STDIN>;
    if ($answer =~ /^y/i) {
        unlink $file;
        print "Deleted\n";
    }
}

七、实践练习 #

练习1:目录树 #

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

my $dir = $ARGV[0] // ".";

find(sub {
    my $depth = () = $File::Find::name =~ /\//g;
    my $indent = "  " x $depth;
    my $name = -d $_ ? "$_/" : $_;
    print "$indent$name\n";
}, $dir);

练习2:文件搜索 #

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

my ($dir, $pattern) = @ARGV;
die "Usage: $0 <dir> <pattern>\n" unless $dir && $pattern;

my @found;

find(sub {
    push @found, $File::Find::name if /$pattern/;
}, $dir);

if (@found) {
    say "Found:";
    say "  $_" foreach @found;
} else {
    say "No files found matching $pattern";
}

练习3:目录同步 #

perl
#!/usr/bin/perl
use strict;
use warnings;
use v5.10;
use File::Copy;
use File::Find;
use File::Path qw(make_path);
use File::Basename;

my ($src, $dst) = @ARGV;
die "Usage: $0 <source> <dest>\n" unless $src && $dst;

find(sub {
    my $src_path = $File::Find::name;
    my $rel_path = File::Spec->abs2rel($src_path, $src);
    my $dst_path = File::Spec->catfile($dst, $rel_path);
    
    if (-d $src_path) {
        make_path $dst_path unless -d $dst_path;
    } else {
        my $dst_dir = dirname($dst_path);
        make_path $dst_dir unless -d $dst_dir;
        
        copy $src_path, $dst_path or warn "Cannot copy $src_path: $!";
        say "Copied: $src_path -> $dst_path";
    }
}, $src);

八、总结 #

本章学习了:

  • 目录遍历(opendir、readdir、glob)
  • 目录创建和删除
  • 路径操作
  • File::Find模块
  • Path::Tiny模块

下一章将学习正则表达式。

最后更新:2026-03-27