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