Perl的文件测试运算符-f为符号链接返回true

Uno*_*nos 17 unix shell perl find

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

-f  File is a plain file.
Run Code Online (Sandbox Code Playgroud)

假设我有一个名为一个文件的目录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 ./
Run Code Online (Sandbox Code Playgroud)

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

%  find . -type f
./file1
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

Adr*_*onk 17

测试符号链接时,除非使用-l符号链接测试,否则将对符号链接指向的内容执行测试.

statlstatLinux系统调用的行为类似.也就是说,如果你stat是一个符号链接,你将得到符号链接目标的结果,而如果你lstat是符号链接,你将获得符号链接本身的结果.这种行为是故意的,因此天真的程序不必关心符号链接,符号链接只会按预期工作.

您应该发现,如果您的符号链接引用目录,则-f测试为false且-d测试为true.


Gre*_*con 4

快速解决方案

\n\n
$ ls | perl -lne \'打印 if stat && -f _\'\n1\n2\n3\n4\n5\nfile1\n\n$ ls | perl -lne \'打印 if lstat && -f _\'\nfile1
\n\n

符号链接并查找

\n\n

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

\n\n
\n

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

\n\n

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

\n\n

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

\n\n

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

\n\n

-follow
\n 此选项构成 \xe2\x80\x9c 表达式\xe2\x80\x9d 的一部分,并且必须在文件名之后指定,但它在其他方面等效于-L. 该-follow选项仅影响命令行中出现在其后面的那些测试。此选项已弃用。如果可能,您应该改用-L

\n
\n\n

将 find 命令转换为 Perl

\n\n

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

\n\n
$find2perl . -类型 f | perl\n./file1
\n\n

我们可以要求提供的文件要么是纯文件本身,要么是纯文件的链接。

\n\n
$find2perl . -follow -输入 f | perl\n./1\n./2\n./3\n./4\n./5\n./file1
\n\n

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

\n\n
sub wanted {\n    my ($dev,$ino,$mode,$nlink,$uid,$gid);\n\n    (($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_)) &&\n    -f _\n    && print("$name\\n");\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

但有了-follow,我们得到

\n\n
sub wanted {\n    my ($dev,$ino,$mode,$nlink,$uid,$gid);\n\n    (($dev,$ino,$mode,$nlink,$uid,$gid) = stat($_)) &&\n    -f _\n    && print("$name\\n");\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

请注意,唯一的区别在于是否wanted调用statlstat,后者记录为

\n\n
\n

lstat EXPR
\n lstat

\n\n

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

\n\n

如果省略EXPR$_ ,则 stats 。

\n
\n\n

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

\n\n

那个有趣的_代币

\n\n

上面快速解决方案的末尾是文档_提到的特殊文件句柄。它保存最新结果的副本,或者作为避免重复进行这些昂贵的系统调用的一种方式。文件测试运算符(例如、、、 )也填充此缓冲区:lstatstatlstat-f-r-e-l

\n\n
\n

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

\n\n
print "Can do.\\n" if -r $a || -w _ || -x _;\n\nstat($filename);\nprint "Readable\\n" if -r _;\nprint "Writable\\n" if -w _;\nprint "Executable\\n" if -x _;\n
Run Code Online (Sandbox Code Playgroud)\n
\n