Sur
[ -f "$file" ]
le site [
La commande fait un stat()
(pas lstat()
) sur le chemin stocké dans $file
et retourne vrai si cet appel système réussit et que le type de fichier retourné par stat()
est " régulier ".
Donc si [ -f "$file" ]
renvoie vrai, vous pouvez dire que le fichier existe et qu'il s'agit d'un fichier normal ou d'un lien symbolique se résolvant éventuellement en un fichier normal (ou du moins, il l'était au moment de l'exécution de la commande stat()
).
Cependant, si elle renvoie faux (ou si [ ! -f "$file" ]
o ! [ -f "$file" ]
retourne vrai), il existe de nombreuses possibilités différentes :
- le fichier n'existe pas
- le fichier existe mais n'est pas un régulier fichier (peut être un périphérique, fifo, répertoire, socket...)
- le fichier existe mais vous n'avez pas le droit de recherche dans le répertoire parent
- le fichier existe mais le chemin pour y accéder est trop long
- le fichier est un lien symbolique vers un fichier normal, mais vous n'avez pas le droit de recherche sur certains des répertoires impliqués dans la résolution du lien symbolique.
- ... toute autre raison pour laquelle le
stat()
l'appel système peut échouer.
En bref, il devrait l'être :
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
Pour être sûr que le fichier n'existe pas, nous aurions besoin de l'attribut stat()
pour retourner avec un code d'erreur de ENOENT
( ENOTDIR
nous indique qu'un des composants du chemin n'est pas un répertoire est un autre cas où nous pouvons dire que le fichier n'existe pas par ce chemin). Malheureusement, le [
Le commandement ne nous le fait pas savoir. Elle renvoie false, que le code d'erreur soit ENOENT, EACCESS (permission denied), ENAMETOOLONG ou tout autre code.
El [ -e "$file" ]
Le test peut également être effectué avec ls -Ld -- "$file" > /dev/null
. Dans ce cas, ls
vous dira pourquoi le stat()
a échoué, bien que l'information ne puisse pas être facilement utilisée de manière programmatique :
$ 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
Au moins ls
me dit que ce n'est pas parce que le fichier n'existe pas qu'il échoue. C'est parce qu'il ne peut pas dire si le fichier existe ou non. Le site [
Le commandement a simplement ignoré le problème.
Avec le zsh
vous pouvez interroger le code d'erreur à l'aide de la commande $ERRNO
variable spéciale après l'échec [
et décoder ce numéro à l'aide de la commande $errnos
dans le tableau spécial de l zsh/system
module :
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
(attention à la $errnos
ne fonctionnait pas avec certaines versions de zsh
lorsqu'il est construit avec des versions récentes de gcc
).
209 votes
J'ai trouvé ceci liste des instructions conditionnelles de bash très utile.
11 votes
Étant la personne très paresseuse que je suis, j'aurais typiquement utilisé la construction de contournement stupide suivante :
if [ -f $FILE ]; then; else; echo "File $FILE does not exist."; fi;
C'est probablement une bonne chose que j'aie trouvé cette question à la place et que j'aie appris à le faire d'une manière plus correcte :)6 votes
Pour être pendulaire, vous devriez dire "fichier ordinaire", car la plupart des documents UNIX/POSIX font référence de manière générique à tous les types d'entrées du système de fichiers par le simple terme "fichiers", par exemple, un lien symbolique est un type de fichier, tout comme un tuyau nommé, un fichier ordinaire, un répertoire, un bloc spécial, un caractère spécial, une socket, etc.
11 votes
@kevinarpe si vous voulez tester si quelque chose existe, utilisez
-e
. -f ne récupère pas les répertoires, les liens symboliques, etc.15 votes
Pour être sûr, utilisez toujours des guillemets doubles pour gérer correctement les noms de fichiers avec des espaces, par exemple,
FILE=$1
->FILE="$1"
yif [ -f $FILE ];
->if [ -f "$FILE" ];
0 votes
Malheureusement, cette méthode est sujette à des conditions de course et n'est pas utile dans la plupart des cas, à l'exception d'exemples triviaux de type "hello world".
1 votes
A propos de votre variable "$FILE" : Par convention, les variables d'environnement (PATH, EDITOR, SHELL, ...) et les variables internes de l'interpréteur de commandes (BASH_VERSION, RANDOM, ...) sont entièrement capitalisées. Tous les autres noms de variables doivent être en minuscules. Puisque les noms de variables sont sensibles à la casse, cette convention évite d'écraser accidentellement les variables d'environnement et internes.
0 votes
Duplicata possible de Bash : Comment vérifier si certains fichiers existent ?
3 votes
@jww C'est le contraire de cette question. Il s'agit de demander une syntaxe spécifique.