В
[ -f "$file" ]
[
команда делает stat()
(не lstat()
) системный вызов на пути , хранящегося в$file
и возвращает истинное если системный вызов завершается успешно и тип файла, возвращаемый stat()
является « регулярным ».
Таким образом, если [ -f "$file" ]
возвращает true, вы можете сказать, что файл существует и является обычным файлом или символической ссылкой, в конечном итоге преобразующейся в обычный файл (или, по крайней мере, это было во время stat()
).
Однако, если он вернется false (или if [ ! -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
).