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