在Perl中如何检测符号链接是否损坏?

8
我想使用Perl在一个目录中删除一个损坏的符号链接。
我认为只需要列出目录中的文件并测试是否是符号链接(-l),如果返回false就解除链接即可。
但是,当使用readir列出所有文件时,我的损坏符号链接没有被识别为文件。因为我的链接指向不存在的东西,我理解为什么会这样。 $myDir中的所有文件都是符号链接,无论是有效的还是损坏的。当我显示@files时,我只得到了有效符号链接的列表。
opendir DIR, $myDir;
my @files = grep(/$regexp/,readdir(DIR));
closedir DIR;
print "filenames : @files\n";

你说的“不被识别为文件”是什么意思?是哪里出了问题?请展示你的代码。 - ysth
7个回答

9
有两个主要相关的系统调用,stat()lstat()lstat() 调用将告诉您它是一个符号链接(但在其他文件上,行为与 stat() 相同)。这使您可以确定该名称是符号链接。 stat() 系统调用跟随符号链接到其末端,并告诉您链接末端的文件(或目录)的信息。如果 stat() 调用在符号链接上失败,则符号链接已损坏或您正在尝试访问没有权限的目录或文件。
Perl 的文件测试操作符包括 -l 以检测名称是否为符号链接。您可以明确使用 Perl 函数 statlstat。在这些函数之间,您应该能够确定符号链接是否损坏,但最好编写一个函数来完成此任务。
您可能不需要使用 Perl 函数 readlink。请注意底层的系统readlink()调用;它不返回空终止的字符串!
有趣的是,Perl 及其 POSIX 模块都不支持realpath()函数。但是,PathTools模块支持它。如果 realpath 失败,则符号链接无法正常工作(也称为损坏)。

8

结合 lstatstat

say "dangling link at $fn" if (lstat $fn and not stat $fn);

更新:它对我有效...

salva@topo:~/t/dl$ perl -E 'opendir $dh, "."; say $_ for grep { !stat $_ and lstat $_ } readdir $dh'
foo
salva@topo:~/t/dl$ ls -l
total 0
-rw-rw-r-- 1 salva salva  0 2011-07-05 12:34 f
lrwxrwxrwx 1 salva salva 11 2011-07-05 12:00 fii -> /etc/shadow
lrwxrwxrwx 1 salva salva 12 2011-07-05 11:59 foo -> /etc/hjdkshf

6

我曾使用以下代码来删除损坏的链接:

chdir $dir        or die;
opendir(DIR, '.') or die;

foreach my $link (readdir DIR) {
  next unless -l $link and not -e readlink($link);

  print "Removing broken link $link\n";
  unlink $link;
}

closedir DIR;

请注意,包含链接的目录必须是当前目录。 readdir 仅返回文件名,并且链接可能是相对路径。

1
+1:关于名称的相对性,这是一个很好的观点。您可以将readdir的值添加到目录的名称(相对或绝对),然后从那里工作:next unless -l“$dir/$link”and not -e“$dir/$link”。在那个重写中,我省略了readlink;我认为这是可以的,因为操作系统会为您读取链接。 - Jonathan Leffler

3

检查损坏的符号链接(如果有符号链接到符号链接,则仅检查顶层):

use strict;
use warnings;
use autodie;
opendir my $dirh, '.';
while (my $file = readdir $dirh) {
    if ( -l $file ) {
        my $target = readlink $file;
        if ( ! -e $target && ! -l $target ) {
            print "$file -> $target broken\n";
        }
    }
}

2
使用readlink()stat()函数来获取结果。

0

损坏的符号链接是一个不存在的链接(-l),它不存在(!-e

perl -e 'print "broken: $_\n" for grep { -l and ! -e } glob("*");'

0
使用内置的Perl glob函数? 例如:
 @files = <*>;
 foreach $file (@files) {
   print $file . "\n";
 }

针对特定的$dir
 @files = <$dir*>;
 foreach $file (@files) {
   print $file . "\n";
 }

如果$dir包含在glob中是特殊字符,例如空格或*?,那么这将无法正常工作。 - melpomene

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接