2

我正在使用 Perl 的 File::Find 模块来扫描文件、目录和链接。除其他外,我希望我正在编写的实用程序报告损坏的(悬在 File::Find 的说法中)符号链接。理论上,这是通过创建一个子例程来支持的,当发现断开的链接时调用该子例程,并find使用适当值的哈希引用调用该方法,例如:

my %options = (
   wanted            => \&ProcessFile,
   follow            => 1,
   follow_skip       => 2,
   dangling_symlinks => \&Dangling
);

find(\%options, @ARGV);

尽管故意创建一个断开的链接来测试它,但 File::Find 永远不会调用 subroutine Dangling。除此功能外,其他所有功能均有效,即ProcessFile按预期调用子程序,跟踪链接等。

4

3 回答 3

2

我做了一个快速测试来弄清楚悬挂符号链接表现出什么行为,结果表明符号链接的定义是我所能理解的

  1. -l 返回真
  2. -e 返回 undef # 因为 -e 作用于链接文件

因此,使用 File::Find::Rule 你似乎想要做的事情相对简单:

#!/usr/bin/perl 

use strict;
use warnings;
use File::Find::Rule ();

my @files = File::Find::Rule->symlink->exec(sub{ !-e $_ })->in('/tmp/test');

print "$_,\n" for @files;

此代码片段能够检测到我能分辨出的所有损坏的符号链接。

如果您希望我运行的测试得出以下结论:

#!/usr/bin/perl 

use strict;
use warnings;
use File::Path ();
use Carp       ();

my $testdir = "/tmp/test";

# Generating test

# Making Dirs
dirmk($_)
  for (
    qw(
    /realdir/
    /deleteddir/
    )
  );

#"Touching" some files
generate($_)
  for (
    qw(
    /realfile
    /deletedfile
    /realdir/realfile
    /realdir/deletedfile
    /deleteddir/afile
    )
  );

# Symlink them
{
    lns( '/realfile',            '/realfile_symlink' );
    lns( '/deletedfile',         '/deletedfile_symlink' );
    lns( '/realdir',             '/realdir_symlink' );
    lns( '/deleteddir',          '/deleteddir_symlink' );
    lns( '/realdir/realfile',    '/realdir_realfile_symlink' );
    lns( '/realdir/deletedfile', '/realdir_deletedfile_symlink' );
    lns( '/deleteddir/afile',    '/deleteddir_file' );
}

# Make the deletions
del($_)
  for (
    qw(
    /deletedfile
    /deleteddir/afile
    /realdir/deletedfile
    /deleteddir/
    )
  );

statify($_)
  for (
    '', qw(
    /realfile
    /realfile_symlink
    /deletedfile_symlink
    /realdir
    /realdir_symlink
    /deleteddir_symlink
    /realdir/realfile
    /realdir_realfile_symlink
    /realdir_deletedfile_symlink
    /deleteddir_file
    )
  );

sub statify {
    my $fn = $testdir . shift;
    printf(
        "r: %3s e: %3s s: %3s f: %3s d: %3s l: %3s | %s \n",
        -r $fn || 0,
        -e $fn || 0,
        -s $fn || 0,
        -f $fn || 0,
        -d $fn || 0,
        -l $fn || 0,
        $fn
    );

}

sub generate {
    my $fn = $testdir . shift;
    open my $fh, '>', $fn or Carp::croak("Error Creating $fn $! $@");
    print $fh "This is $fn \n";
    close $fh or Carp::carp("Error on close for $fn $! $@");
    return;
}

sub lns {
    my $x = $testdir . shift;
    my $y = $testdir . shift;
    if ( -e $y ) {
        unlink $y;
    }
    symlink $x, $y or Carp::croak("Error ln $x => $y , $! $@");
}

sub del {
    my $fn = $testdir . shift;
    if ( -f $fn ) {
        unlink $fn;
    }
    if ( -d $fn ) {
        rmdir $fn;
    }
}

sub dirmk {
    my $fn = $testdir . shift;
    File::Path::mkpath($fn);
}

这是输出:

r:1 e:1 s:220 f:0 d:1 l:0 | /tmp/测试
r:1 e:1 s:28 f:1 d:0 l:0 | /tmp/test/realfile
r:1 e:1 s:28 f:1 d:0 l:1 | /tmp/test/realfile_symlink
r:0 e:0 s:0 f:0 d:0 l:1 | /tmp/test/deletedfile_symlink
r: 1 e: 1 s: 60 f: 0 d: 1 l: 0 | /tmp/test/realdir
r:1 e:1 s:60 f:0 d:1 l:1 | /tmp/test/realdir_symlink
r:0 e:0 s:0 f:0 d:0 l:1 | /tmp/test/deleteddir_symlink
r:1 e:1 s:36 f:1 d:0 l:0 | /tmp/test/realdir/realfile
r:1 e:1 s:36 f:1 d:0 l:1 | /tmp/test/realdir_realfile_symlink
r:0 e:0 s:0 f:0 d:0 l:1 | /tmp/test/realdir_deletedfile_symlink
r:0 e:0 s:0 f:0 d:0 l:1 | /tmp/test/deleteddir_file
于 2009-03-26T20:07:43.707 回答
2

test.pl在我的主目录中创建:

#!/usr/bin/perl

use File::Find;

my %options = ( wanted => \&ProcessFile,
                follow => 1,
                follow_skip => 2,
                dangling_symlinks => \&Dangling );

find(\%options, @ARGV);

sub ProcessFile {
  print "ProcessFile ($File::Find::name in $File::Find::dir)\n";
}

sub Dangling {
  my ($name, $dir) = @_;
  print "Dangling ($name in $dir)\n";
}

然后:

    $ chmod 755 测试.pl

    $ mkdir /tmp/findtest
    $ cd /tmp/findtest
    $ ln -s /tmp/doesnotexist linkylink
    $ ~/test.pl 。

结果是:

ProcessFile (. in .)
悬空(./ 中的链接链接)
ProcessFile (./linkylink in .)
于 2009-03-26T18:16:47.080 回答
1

我喜欢File::Find::Rule在使用中看到,但在这里没有区别。

话虽如此,

$ mkdir test
$ cd test
$ ln -s a b
$ perl -w -MFile::Find -e'find({wanted=>sub{print"wanted $_\n"},dangling_symlinks=>sub{print"dangling $_[0] in $_\n"},follow=>1},".")'
wanted .
dangling b in .
wanted b

为我工作。

是什么perl -MFile::Find -e'print"$File::Find::VERSION\n"'

更新

查看 Perl 的 RT,我发现#28929: File::Find follow_fast => 1 lost dangling symlink。它显然会影响File::Find1.07 及更早版本,该版本与 Perl 5.8.7 及更早版本捆绑在一起(以及 5.9.x 开发线中的 5.9.1 及更早版本)。

我建议您说服您的系统管理员更新 Perl,或至少更新一些模块(并File::Find::Rule在他们使用时添加),但如果失败,您可以制作自己的$PERL5LIB并将更新的模块放在那里。

于 2009-03-26T18:22:02.790 回答