我曾经认为-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,它会按预期提供输出:
% 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
符号链接是否也被视为普通文件?如果是这样,为什么?我对文件测试的使用是否不正确?
答案 0 :(得分:17)
当您测试符号链接时,除非您使用-l
符号链接测试,否则将对符号链接指向的内容执行测试。
这与行为相似的stat
和lstat
Linux系统调用相似。也就是说,如果您stat
符号链接,您将获得符号链接目标的结果,而如果lstat
符号链接,您将获得符号链接本身的结果。这种行为是故意的,因此天真的程序不必关心符号链接,符号链接将按预期工作。
您应该会发现,如果您的符号链接引用目录,则-f
测试为false且-d
测试为真。
答案 1 :(得分:4)
$ ls | perl -lne 'print if stat && -f _' 1 2 3 4 5 file1 $ ls | perl -lne 'print if lstat && -f _' file1
默认情况下,GNU find
从不取消引用或遵循符号链接,但find
documentation描述了更改此政策的开关。
控制关于链接的find行为的选项如下: -
-P
find
根本不会取消引用符号链接。这是默认行为。必须在命令行上的任何文件名之前指定此选项。
-H
find
不会取消引用符号链接(除了命令行上的文件名,它们被解除引用)。如果无法解除引用符号链接,则使用符号链接本身的信息。必须在命令行上的任何文件名之前指定此选项。
-L
find
在可能的情况下解除引用符号链接,并且在不可能的情况下,它使用符号链接本身的属性。必须在命令行上的任何文件名之前指定此选项。使用此选项还意味着与-noleaf
选项具有相同的行为。如果您以后使用-H
或-P
选项,则不会关闭-noleaf
。
-follow
此选项构成“表达式”的一部分,必须在文件名后指定,但它等同于-L
。-follow
选项仅影响在命令行上显示的那些测试。不推荐使用此选项。如果可能,您应该使用-L
代替。
标准发行版附带一个find2perl
实用程序,该实用程序与旧版Unix系统中的find
兼容。
$ find2perl . -type f | perl ./file1
我们可以要求提供纯文件本身或普通文件链接的文件。
$ find2perl . -follow -type f | perl ./1 ./2 ./3 ./4 ./5 ./file1
在生成的代码find2perl
中,传递给find
from the File::Find module的默认wanted
子信息是
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
函数相同(包括设置特殊_
文件句柄)但stats是符号链接而不是符号链接指向的文件。如果您的系统未实现符号链接,则会执行正常的stat
。有关更详细的信息,请参阅stat
的文档。如果省略 EXPR ,则统计信息
$_
。
从find2perl
显示的示例输出显示,您可以使用filetest运算符表达您的意图,但要精确了解符号链接的语义,并选择stat
与lstat
。
_
令牌上面快速解决方案末尾的_
是lstat
documentation提到的特殊文件句柄。它包含stat
或lstat
的最新结果的副本,以避免重复进行那些昂贵的系统调用。 -f
,-r
,-e
和-l
等Filetest operators也会填充此缓冲区:
如果任何文件测试(或
stat
或lstat
运算符)被赋予由单独下划线组成的特殊文件句柄,那么前一个文件测试的统计结构(或{{使用1}}运算符),保存系统调用。 (这不适用于stat
,您需要记住-t
和lstat
在符号链接的统计结构中保留值,而不是真实文件。(另外,如果统计缓冲区被-l
调用填充,lstat
和-T
将使用-B
的结果重置它。例如:stat _
答案 2 :(得分:3)
默认情况下,所有文件测试运算符(-l
除外)都使用stat()
进行测试,这意味着它们对符号链接是透明的。 -f
在常规文件上返回true,或在常规文件中返回符号链接。
为了使用lstat()
,您应首先lstat
然后使用特殊_
文件句柄上的文件测试,该文件句柄存储来自最新stat
的结果或lstat
操作。
perl -e 'while(<>) { chomp; print "$_\n" if lstat $_ && -f _ }'