18

我曾经认为-f测试一个文件是为了看它是否是一个普通文件,而不是其他任何东西。但是 Perl 的行为似乎有所不同。我查了perldoc entry ,它说:

-f  File is a plain file.

假设我有一个目录,其中包含一个名为 的文件file1和 5 个符号链接1 2 3 4 5,每个链接都指向file1,如下所示:

-rw-r--r-- file1
lrwxrwxrwx 1 -> file1
lrwxrwxrwx 2 -> file1
lrwxrwxrwx 3 -> file1
lrwxrwxrwx 4 -> file1
lrwxrwxrwx 5 -> file1
drwxr-xr-x ../
drwxr-xr-x ./

如果我使用过滤器在此目录上运行 find -type f,它会按预期提供输出:

%  find . -type f
./file1

但是当我使用 -f 运算符运行 perl 脚本时,它会给出以下输出:

%  ls | perl -e 'while(<>) { chomp; print "$_\n" if -f $_ }'
1
2
3
4
5
file1

当我添加测试时-l,它按预期工作:

%  ls | perl -e 'while(<>) { chomp; print "$_\n" if -f $_ and not -l $_}'
file1

符号链接也被视为普通文件吗?如果是这样,为什么?我对文件测试的使用不正确吗?

4

3 回答 3

17

测试符号链接时,除非您使用符号链接测试,否则将对符号链接指向的事物进行-l测试。

这与行为相似的statLinuxlstat系统调用相似。也就是说,如果您stat是符号链接,您将获得符号链接目标的结果,而如果您lstat是符号链接,您将获得符号链接本身的结果。这种行为是有意的,因此天真的程序不必关心符号链接,符号链接将按预期工作。

您应该会发现,如果您的符号链接指向一个目录,则-f测试为假而-d测试为真。

于 2012-04-25T09:25:49.407 回答
4

快速解决方案

$ ls | perl -lne '打印如果 stat && -f _'
1
2
3
4
5
文件 1

$ ls | perl -lne '打印如果 lstat && -f _'
文件 1

符号链接和查找

默认情况下,GNUfind从不取消引用或遵循符号链接,但find文档描述了更改此策略的开关。

控制 find 相对于链接的行为的选项如下:-

-P
find根本不取消引用符号链接。这是默认行为。此选项必须在命令行上的任何文件名之前指定。

-H
find不取消引用符号链接(命令行上的文件名被取消引用的情况除外)。如果无法取消引用符号链接,则使用符号链接本身的信息。此选项必须在命令行上的任何文件名之前指定。

-L
find在可能的情况下取消引用符号链接,在不可能的情况下,它使用符号链接本身的属性。此选项必须在命令行上的任何文件名之前指定。使用此选项也意味着与该选项相同的行为-noleaf。如果您稍后使用-Hor-P选项,这不会关闭-noleaf

-follow
此选项构成“表达式”的一部分,必须在文件名之后指定,但在其他方面等价于-L. 该-follow选项仅影响出现在命令行上的那些测试。此选项已弃用。在可能的情况下,您应该-L改用。

将查找命令转换为 Perl

标准发行版附带一个与旧 Unix 系统find2perl兼容的实用程序。find

$ find2perl 。-类型 f | perl
./file1

相反,我们可以要求文件本身是纯文件或指向纯文件的链接。

$ find2perl 。-follow -type f | perl
./1
./2
./3
./4
./5
./file1

在代码生成中,从 File::Find 模块传递给find2perl的默认子是wantedfind

sub wanted {
    my ($dev,$ino,$mode,$nlink,$uid,$gid);

    (($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_)) &&
    -f _
    && print("$name\n");
}

但是有了-follow,我们得到

sub wanted {
    my ($dev,$ino,$mode,$nlink,$uid,$gid);

    (($dev,$ino,$mode,$nlink,$uid,$gid) = stat($_)) &&
    -f _
    && print("$name\n");
}

请注意,唯一的区别是wanted调用stat还是lstat,后者被记录为

lstat EXPR
lstat

与函数执行相同的操作stat(包括设置特殊_文件句柄),但统计符号链接而不是符号链接指向的文件。如果您的系统上未实现符号链接,则正常运行stat。有关更多详细信息,请参阅stat.

如果省略EXPR$_ ,则为 stats 。

正如示例输出所示,您可以使用 filetest 运算符表达您的意图,但可以通过选择vsfind2perl来精确了解符号链接的语义。statlstat

那个有趣的_令牌

上述_快速解决方案的末尾是lstat文档提到的特殊文件句柄。它保存了最新结果的副本,stat或者lstat作为避免重复进行那些昂贵的系统调用的一种方式。Filetest 操作符,例如-f, -r, -e,-l也填充这个缓冲区:

如果任何文件测试(或statorlstat运算符)被赋予由单独下划线组成的特殊文件句柄,则使用前一个文件测试(或stat运算符)的 stat 结构,保存系统调用。(这不适用于-t,您需要记住这一点,lstat并将-l值保留在符号链接的 stat 结构中,而不是真正的文件中。)(此外,如果 stat 缓冲区被lstat调用填充,-T并将-B重置它的结果stat _)。例子:

print "Can do.\n" if -r $a || -w _ || -x _;

stat($filename);
print "Readable\n" if -r _;
print "Writable\n" if -w _;
print "Executable\n" if -x _;
于 2012-04-25T15:19:47.077 回答
3

默认情况下,所有文件测试运算符(除了-l)都stat()用于测试,这意味着它们对符号链接是透明的。-f在常规文件上返回 true,或在常规文件上返回符号链接。

为了使用它lstat(),您应该首先lstat在特殊文件句柄上使用文件测试,该_文件句柄存储最近statlstat操作的结果。

perl -e 'while(<>) { chomp; print "$_\n" if lstat $_ && -f _ }'
于 2012-04-25T16:12:49.467 回答