Perl 的文件测试运算符 -f 对符号链接返回 true
Posted
技术标签:
【中文标题】Perl 的文件测试运算符 -f 对符号链接返回 true【英文标题】:Perl's file test operator -f returns true for symbolic links 【发布时间】:2012-05-05 22:33:04 【问题描述】:我曾经认为-f
测试一个文件是为了看它是否是一个普通文件,而不是其他任何东西。但是 Perl 的行为似乎有所不同。我查看了 perldoc 条目,它说:
-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 ./
如果我使用-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
符号链接是否也被视为普通文件?如果是这样,为什么?我对文件 test 的使用不正确吗?
【问题讨论】:
相关:-x
是stat
系统调用的快捷方式。如果想自己区分符号链接,请使用lstat
。
【参考方案1】:
当您测试符号链接时,除非您使用 -l
符号链接测试,否则将对符号链接指向的对象进行测试。
这与行为相似的stat
和lstat
Linux 系统调用相似。也就是说,如果您 stat
符号链接,您将获得符号链接目标的结果,而如果您 lstat
符号链接,您将获得符号链接本身的结果。这种行为是有意的,因此天真的程序不必关心符号链接,符号链接将按预期工作。
您应该发现,如果您的符号链接指向一个目录,-f
测试为假,-d
测试为真。
【讨论】:
+1。perldoc stat
, perdoc lstat
【参考方案2】:
快速解决方案
$ ls | perl -lne '打印如果 stat && -f _' 1 2 3 4 5 文件 1 $ ls | perl -lne '打印如果 lstat && -f _' 文件1
符号链接和查找
默认情况下,GNU find
从不取消引用或遵循符号链接,但 find
documentation 描述了更改此策略的开关。
控制 find 与链接相关的行为的选项如下:-
-P
find
根本不取消引用符号链接。这是默认行为。此选项必须在命令行上的任何文件名之前指定。
-H
find
不会取消引用符号链接(命令行上的文件名除外,它们会被取消引用)。如果无法取消引用符号链接,则使用符号链接本身的信息。此选项必须在命令行上的任何文件名之前指定。
-L
find
在可能的情况下取消引用符号链接,在不可能的情况下,它使用符号链接本身的属性。此选项必须在命令行上的任何文件名之前指定。使用此选项也意味着与-noleaf
选项相同的行为。如果您稍后使用-H
或-P
选项,这不会关闭-noleaf
。
-follow
此选项构成“表达式”的一部分,必须在文件名之后指定,但在其他方面等同于-L
。-follow
选项仅影响出现在命令行上的那些测试。此选项已弃用。如果可能,您应该改用-L
。
将查找命令转换为 Perl
标准发行版附带一个 find2perl
实用程序,它与旧 Unix 系统中的 find
兼容。
$ find2perl 。 -类型 f | perl ./file1
我们可以改为要求文件本身是纯文件或指向纯文件的链接。
$ find2perl 。 -follow -type f | perl ./1 ./2 ./3 ./4 ./5 ./file1
在find2perl
生成的代码中,默认wanted
子传递给find
from the File::Find module是
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 <i>EXPR</i>
lstat
与
stat
函数做同样的事情(包括设置特殊的_
文件句柄)但统计符号链接而不是符号链接指向的文件。如果您的系统上未实现符号链接,则正常的stat
已完成。有关更多详细信息,请参阅stat
的文档。如果省略EXPR,则统计
$_
。
正如 find2perl
的示例输出所示,您可以使用 filetest 运算符表达您的意图,但可以通过选择 stat
与 lstat
来准确了解符号链接的语义。
那个有趣的_
令牌
上述快速解决方案末尾的_
是lstat
documentation 提到的特殊文件句柄。它保存了来自stat
或lstat
的最新结果的副本,以避免重复进行那些昂贵的系统调用。 Filetest operators 如-f
、-r
、-e
和-l
也填充此缓冲区:
如果任何文件测试(或
stat
或lstat
运算符)被赋予由单独下划线组成的特殊文件句柄,则前一个文件测试(或stat
运算符)的统计结构是使用,保存系统调用。 (这不适用于-t
,您需要记住lstat
和-l
将值留在符号链接的统计结构中,而不是真实文件中。)(另外,如果统计缓冲区已满通过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 _;
【讨论】:
我认为这里的简短版本是,如果您按名称进行文件测试,则查找将始终遵循符号链接,但如果您使用stat
或 lstat
加上 _
句柄上的文件测试,你可以控制是否关注。
@hobbs 感谢您的微妙建议!已更新。
从 Adrian 的回答中,我了解到所有的魔法/逻辑都在于 stat
和 lstat
函数。但后来我无法完全关联find
的结果,而man find
有点难以理解。非常感谢文档链接和解释。【参考方案3】:
默认情况下,所有文件测试运算符(-l
除外)都使用stat()
进行测试,这意味着它们对符号链接是透明的。 -f
在常规文件或常规文件的符号链接上返回 true。
要改用lstat()
,您应该首先使用lstat
,然后在特殊的_
文件句柄上使用文件测试,该文件句柄存储最近的stat
或lstat
操作的结果。
perl -e 'while(<>) chomp; print "$_\n" if lstat $_ && -f _ '
【讨论】:
以上是关于Perl 的文件测试运算符 -f 对符号链接返回 true的主要内容,如果未能解决你的问题,请参考以下文章
使用Unix readlink命令的Perl递归循环符号链接最终目的地