En
[ -f "$file" ]
el [
comando realiza una stat()
(no lstat()
) llamada al sistema en la ruta almacenada $file
y devuelve verdadero si esa llamada al sistema tiene éxito y el tipo de archivo que devuelve stat()
es " regular ".
Entonces, si [ -f "$file" ]
devuelve verdadero, puede decir que el archivo existe y que es un archivo normal o un enlace simbólico que finalmente se resuelve en un archivo normal (o al menos lo era en el momento de stat()
).
Sin embargo si devuelve falso (o si [ ! -f "$file" ]
o ! [ -f "$file" ]
devolver true), hay muchas posibilidades diferentes:
- el archivo no existe
- el archivo existe pero no es un archivo normal (podría ser un dispositivo, quince, directorio, socket ...)
- el archivo existe pero no tiene permiso de búsqueda para el directorio padre
- el archivo existe pero esa ruta para acceder es demasiado larga
- el archivo es un enlace simbólico a un archivo normal, pero no tiene permiso de búsqueda para algunos de los directorios involucrados en la resolución del enlace simbólico.
- ... cualquier otra razón por la cual la
stat()
llamada al sistema puede fallar.
En resumen, debería ser:
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
Para saber con certeza que el archivo no existe, necesitaríamos que la stat()
llamada del sistema regrese con un código de error de ENOENT
( ENOTDIR
nos dice que uno de los componentes de la ruta no es un directorio, es otro caso en el que podemos decir que el archivo no existir por ese camino). Desafortunadamente, el[
comando no nos deja saber eso. Devolverá falso si el código de error es ENOENT, EACCESS (permiso denegado), ENAMETOOLONG o cualquier otra cosa.
La [ -e "$file" ]
prueba también se puede hacer con ls -Ld -- "$file" > /dev/null
. En ese caso, ls
le diremos por qué stat()
falló, aunque la información no se puede usar fácilmente mediante programación:
$ 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
Al menos ls
me dice que no es porque el archivo no existe que falla. Es porque no puede decir si el archivo existe o no. El [
comando simplemente ignoró el problema.
Con el zsh
shell, puede consultar el código de error con la $ERRNO
variable especial después del [
comando que falla y decodificar ese número utilizando la $errnos
matriz especial en el zsh/system
módulo:
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
(Tenga en cuenta que el $errnos
soporte se rompió con algunas versiones de zsh
cuando se creó con versiones recientes degcc
).