В
[ -f "$file" ]
команда [
выполняет системный вызов stat()
(не lstat()
) для пути, хранящегося в $file
, и возвращает true , если этот системный вызов выполнен успешно, а тип файла stat()
возвращает " обычный ".
Таким образом, если [ -f "$file" ]
возвращает true, вы можете сказать, что файл существует и является обычным файлом или символической ссылкой, в конечном итоге преобразующейся в обычный файл (или, по крайней мере, во время stat()
). *
Однако, если он возвращает false (или если [ ! -f "$file" ]
или ! [ -f "$file" ]
возвращает true), существует много различных возможностей:
- файл не существует
- файл существует, но не является обычным файлом (это может быть устройство, fifo, каталог, сокет ...)
- файл существует, но у вас нет разрешения на поиск в родительском каталоге
- файл существует, но путь к нему слишком длинный
- файл является символической ссылкой на обычный файл, но у вас нет разрешения на поиск в некоторых каталогах, участвующих в разрешении символической ссылки.
- ... любая другая причина, по которой системный вызов
stat()
может завершиться неудачей.
Короче, должно быть:
if [ -f "$file" ]; then
printf '"%s" is a path to a regular file or symlink to regular file\n' "$file"
elif [ -e "$file" ]; then
printf '"%s" exists but is not a regular file\n' "$file"
elif [ -L "$file" ]; then
printf '"%s" exists, is a symlink but I cannot tell if it eventually resolves to an actual file, regular or not\n' "$file"
else
printf 'I cannot tell if "%s" exists, let alone whether it is a regular file or not\n' "$file"
fi
Чтобы точно знать, что файл не существует, нам потребуется системный вызов stat()
для возврата с кодом ошибки ENOENT
(ENOTDIR
сообщает, что один из компонентов пути не является каталогом это еще один случай, когда мы можем сказать, что файл не существует по этому пути). К сожалению, команда [
не дает нам знать об этом. Он вернет false, независимо от того, является ли код ошибки ENOENT, EACCESS (разрешение отклонено), ENAMETOOLONG или что-либо еще.
Тест [ -e "$file" ]
также можно выполнить с помощью ls -Ld -- "$file" > /dev/null
. В этом случае ls
сообщит вам, почему произошел сбой stat()
, хотя эту информацию нельзя легко использовать программно:
$ file=/var/spool/cron/crontabs/root
$ if [ ! -e "$file" ]; then echo does not exist; fi
does not exist
$ if ! ls -Ld -- "$file" > /dev/null; then echo stat failed; fi
ls: cannot access '/var/spool/cron/crontabs/root': Permission denied
stat failed
По крайней мере, ls
говорит мне, что это не потому, что файл не существует, что он выходит из строя. Это потому, что он не может сказать, существует файл или нет. Команда [
просто проигнорировала проблему.
С помощью оболочки zsh
вы можете запросить код ошибки с помощью специальной переменной $ERRNO
после неудачной команды [
и декодировать это число с помощью специального массива $errnos
в модуле zsh/system
:
zmodload zsh/system
ERRNO=0
if [ ! -f "$file" ]; then
err=$ERRNO
case $errnos[err] in
("") echo exists, not a regular file;;
(ENOENT|ENOTDIR)
if [ -L "$file" ]; then
echo broken link
else
echo does not exist
fi;;
(*) syserror -p "can't tell: " "$err"
esac
fi
(будьте осторожны, поддержка $errnos
была нарушена в некоторых версиях zsh
при сборке с последними версиями gcc
).