为什么File :: Find没有处理我破坏的符号链接?

时间:2009-03-26 18:08:25

标签: perl symlink file-find

我正在使用Perl的File :: Find模块来扫描文件,目录和链接。除此之外,我希望我正在编写的实用程序报告已损坏(悬挂在File :: Find的用语中)符号链接。理论上,这可以通过创建一个子程序来支持,只要找到一个断开的链接,并使用适当值的哈希引用调用find方法,例如:

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

find(\%options, @ARGV);

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

3 个答案:

答案 0 :(得分: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 test.pl

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

结果:

ProcessFile (. in .)
Dangling (linkylink in ./)
ProcessFile (./linkylink in .)

答案 1 :(得分:2)

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

  1. -l返回true
  2. -e返回undef#,因为-e适用于链接文件
  3. 因此,使用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/test 
    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 
    

答案 2 :(得分: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 loses dangling symlink。它显然会影响File::Find 1.07及更早版本,它与Perl 5.8.7及更早版本(以及5.9.x开发行中的5.9.1及更早版本)捆绑在一起。

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