No
[ -f "$file" ]
o [
comando faz uma stat()
(não lstat()
) chamada do sistema no caminho armazenado $file
e retorna true se a chamada do sistema for bem-sucedida e o tipo de arquivo retornado por stat()
" regular ".
Portanto, se [ -f "$file" ]
retorna true, você pode dizer que o arquivo existe e é um arquivo regular ou um link simbólico que eventualmente está sendo resolvido para um arquivo regular (ou pelo menos era no momento do stat()
).
No entanto, se retornar falso (ou se [ ! -f "$file" ]
ou ! [ -f "$file" ]
retornar verdadeiro), há muitas possibilidades diferentes:
- o arquivo não existe
- o arquivo existe, mas não é regular (pode ser um dispositivo, fifo, diretório, soquete ...)
- o arquivo existe, mas você não tem permissão de pesquisa para o diretório pai
- o arquivo existe, mas esse caminho para acessá-lo é muito longo
- o arquivo é um link simbólico para um arquivo normal, mas você não tem permissão de pesquisa para alguns dos diretórios envolvidos na resolução do link simbólico.
- ... qualquer outro motivo pelo qual a
stat()
chamada do sistema possa falhar.
Em suma, deve 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 ter certeza de que o arquivo não existe, precisaríamos que a stat()
chamada do sistema retornasse com um código de erro de ENOENT
( ENOTDIR
nos diz que um dos componentes do caminho não é um diretório, outro caso em que podemos dizer que o arquivo não existe) existir por esse caminho). Infelizmente, o [
comando não nos informa disso. Ele retornará false se o código de erro é ENOENT, EACCESS (permissão negada), ENAMETOOLONG ou qualquer outra coisa.
O [ -e "$file" ]
teste também pode ser feito com ls -Ld -- "$file" > /dev/null
. Nesse caso, ls
informará o motivo da stat()
falha, embora as informações não possam ser facilmente usadas programaticamente:
$ 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
Pelo menos ls
me diz que não é porque o arquivo não existe que falha. É porque ele não pode dizer se o arquivo existe ou não. O [
comando apenas ignorou o problema.
Com o zsh
shell, você pode consultar o código de erro com a $ERRNO
variável especial após o [
comando com falha e decodificar esse número usando a $errnos
matriz especial no 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
(cuidado, o $errnos
suporte foi quebrado com algumas versões de zsh
quando construído com versões recentes dogcc
).