Perl:检查文件(-f与!-d)

chr*_*s01 2 perl file

我有点困惑。我希望-f等于!-d

但似乎并非如此。我尝试检查文件是否为目录。

use File::Find;
find (\&found, ".");

sub found ()
{
  my $fn = $File::Find::name;
  if( ! -d $fn)
  {
    print "\n1 " . $fn;         # a file
  }
  else
  {
    print "\n0 " . $fn;         # a directory
  }
}
Run Code Online (Sandbox Code Playgroud)

结果!-d 是有关测试目录的期望。

0 .
1 ./Whatever.mp3
1 ./x.pl
0 ./bb
1 ./bb/Whatever.mp3
0 ./aa
1 ./aa/Whatever.mp3
Run Code Online (Sandbox Code Playgroud)

-f的结果是意外的。

0 .
1 ./Whatever.mp3
1 ./x.pl
0 ./bb
0 ./bb/Whatever.mp3            # ???
0 ./aa
0 ./aa/Whatever.mp3            # ???
Run Code Online (Sandbox Code Playgroud)

ike*_*ami 7

默认情况下,find在调用回调之前更改为包含文件的目录。

这意味着./bb/Whatever.mp3您应该在通过时通过Whatever.mp3。结果,两个-f-d都返回undef信号以指示发生了错误。

$File::Find::name包含相对于原始CWD的文件路径。另一方面,$_包含相对于CWD的文件路径。这就是您应该使用的。

use File::Find;

find({ wanted => \&found, no_chdir => 1 }, ".");

sub found {
   if (!stat($_)) {
      warn("Can't stat \"$_\": $!\n");
      return;
   }

   if (-d _)  {
      print "0 $_\n;   # A directory
   } else {
      print "1 $_\n";  # Some other kind of file
   }
}
Run Code Online (Sandbox Code Playgroud)

我还包括其他两个更改。

检查错误

我们可以使用

my $rv = -d $_;
if (!defined($rv)) {
   warn("Can't stat \"$_\": $!\n");
   return;
}

if ($rv)  {
   ...
}
Run Code Online (Sandbox Code Playgroud)

但是,由于这些-X函数只是stat系统调用的包装器,因此我发现使用stat前检查错误并使用_其上的特殊句柄避免对进行额外的调用比较干净stat。特殊句柄_告诉-d使用对stat/ 的先前调用的结果-X

避免不必要的来电 chdir

现在您不使用$File::Find::name,所有时间都没有必要find打电话chdir了。这是做什么的no_chdir => 1


要回答您实际提出的问题,有两种情况-f不等于!-d

可能性1:您无法保存stat文件。

-f-d返回三个不同值之一:

  • 未定义:发生错误。
  • 定义且为假:不是纯文件(-f)或目录(-d
  • 定义且正确:这是纯文件(-f)或目录(-d

如果提供的路径并不存在,如果你没有权限读取目录中的文件驻留,或者发生其他一些错误,都-f!-dundef(和$!将包含错误代码/消息)。

可能性2:文件既不是纯文件也不是目录。

纯文件(-f)和目录(-d)只是七种文件类型中的两种。以下是其他:

  • 符号链接(-l
  • 命名管道又名“ fifos”(-p
  • 插座(-S
  • 块设备(-b
  • 字符设备(-c

这七个中只有一个是真实的。


完整文件类型检查示例:

stat($qfn)
   or die("Can't stat \"$qfn\": $!\n");

if    (-f _) { say "\"$qfn\" is a plain file."; }
elsif (-d _) { say "\"$qfn\" is a directory."; }
elsif (-l _) { say "\"$qfn\" is a symbolic link."; }
elsif (-p _) { say "\"$qfn\" is a named pipe."; }
elsif (-S _) { say "\"$qfn\" is a socket."; }
elsif (-b _) { say "\"$qfn\" is a block device."; }
elsif (-c _) { say "\"$qfn\" is a character device."; }
else         { say "\"$qfn\" is of unknown type."; }  # Shouldn't happen on unix systems.
Run Code Online (Sandbox Code Playgroud)