Почему File :: Find не обрабатывает мою неработающую символическую ссылку? - PullRequest
2 голосов
/ 26 марта 2009

Я использую модуль Perl File :: Find для сканирования файлов, каталогов и ссылок. Помимо прочего, я хочу, чтобы утилита, которую я пишу, сообщала о неработающих (болтающихся на языке File :: Find) символических ссылках. Теоретически это поддерживается созданием подпрограммы, которая будет вызываться при обнаружении неработающей ссылки, и вызовом метода find с хеш-ссылкой соответствующих значений, например:

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

find(\%options, @ARGV);

Несмотря на намеренное создание неработающей ссылки для проверки этого, File :: Find never никогда не вызывает подпрограмму Dangling. Все остальное работает, кроме этой функции, то есть подпрограмма ProcessFile вызывается, как и ожидалось, ссылки следуют и т. Д.

Ответы [ 3 ]

2 голосов
/ 26 марта 2009

Я провел быструю проверку, чтобы выяснить, какое поведение демонстрирует висячие символические ссылки, и оказалось, что определение символической ссылки настолько далеко, насколько я могу разобрать

  1. -l возвращает true
  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/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 голосов
/ 26 марта 2009

Создано 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 голос
/ 26 марта 2009

Мне нравится видеть 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"'?

обновление

Просматривая RT Perl, я обнаружил # 28929: File :: Find follow_fast => 1 теряет висячую символическую ссылку . По-видимому, это влияет на File::Find 1.07 и более ранние версии, которые связаны с Perl 5.8.7 и более ранними версиями (а также с 5.9.1 и более ранними версиями в линии разработки 5.9.x).

Я бы посоветовал вам убедить ваших системных администраторов обновить Perl или хотя бы несколько модулей (и добавить File::Find::Rule, пока они у него есть), но если этого не произойдет, вы можете сделать свои $PERL5LIB и разместите там обновленные модули.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...