В
[ -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
).