Perl: проверить файлы (-f против! -D) - PullRequest
2 голосов
/ 15 апреля 2019

Я немного смущен. Я ожидаю, что -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
  }
}

Результаты для ! -d это то, что я ожидал бы в отношении моего test-directory.

0 .
1 ./Whatever.mp3
1 ./x.pl
0 ./bb
1 ./bb/Whatever.mp3
0 ./aa
1 ./aa/Whatever.mp3

Результаты для -f являются неожиданными.

0 .
1 ./Whatever.mp3
1 ./x.pl
0 ./bb
0 ./bb/Whatever.mp3            # ???
0 ./aa
0 ./aa/Whatever.mp3            # ???

Ответы [ 2 ]

7 голосов
/ 15 апреля 2019

По умолчанию 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
   }
}

Я включил два других изменения.

Проверка на ошибки

Мы могли бы использовать

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

if ($rv)  {
   ...
}

Но так как функции -X являются просто оболочками для системного вызова stat, я считаю, что немного проще использовать stat для проверки на наличие ошибок и использовать специальную ручку _, чтобы избежать дополнительных звонков на stat. Специальный дескриптор _ указывает -d использовать результаты предыдущего вызова для stat / -X.

Избегайте ненужных звонков на chdir

Теперь, когда вы не используете $File::Find::name, нет никакого смысла в find звонить chdir все это время. Это то, что делает no_chdir => 1.


Чтобы ответить на вопрос, который вы фактически задали, есть две ситуации, когда -f не равно !-d.

Возможность 1 : Вы не можете stat файл.

-f и -d возвращают одно из трех различных значений:

  • undefined: Произошла ошибка.
  • определено и ложно: это не простой файл (-f) или каталог (-d)
  • определено и верно: это простой файл (-f) или каталог (-d)

Если указанный путь не существует, если у вас нет разрешения на чтение каталога, в котором находится файл, или если возникает какая-либо другая ошибка, то и -f, и !-d будут undef$! будет содержать код ошибки / сообщение).

Возможность 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.
2 голосов
/ 15 апреля 2019

Существует больше типов файлов, чем обычных файлов и каталогов, например, файлы устройств (в иерархии / dev / *).

foreach (qw(/tmp/somefile /tmp/somedir /dev/sda)) {
    print "$_: ";
    print q(-f: true ) if -f;
    print q(-d: true ) if -d;
}
continue {
    print "\n";
}

Если вы запустите приведенный выше код, вы заметите,что для /dev/sda у нас есть и ! -d, и ! -f, поэтому -f не эквивалентно ! -d.

См. также: perlfunc # -X .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...