3586 votes

Comment savoir si un fichier régulier n'existe pas dans Bash ?

J'ai utilisé le script suivant pour voir si un fichier existe :

#!/bin/bash

FILE=$1     
if [ -f $FILE ]; then
   echo "File $FILE exists."
else
   echo "File $FILE does not exist."
fi

Quelle est la syntaxe correcte à utiliser si je veux seulement vérifier si le fichier fait no existent ?

#!/bin/bash

FILE=$1     
if [ $FILE does not exist ]; then
   echo "File $FILE does not exist."
fi

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.

73voto

TechZilla Points 960

Je préfère faire la ligne unique suivante, en POSIX format compatible avec le shell :

$ [ -f "/$DIR/$FILE" ] || echo "$FILE NOT FOUND"

$ [ -f "/$DIR/$FILE" ] && echo "$FILE FOUND"

Pour un couple de commandes, comme je le ferais dans un script :

$  [ -f "/$DIR/$FILE" ] || { echo "$FILE NOT FOUND" ; exit 1 ;}

Une fois que j'ai commencé à faire cela, je n'utilise plus que rarement la syntaxe entièrement typée !

1 votes

Tout d'abord, les références à des variables non citées sont source d'erreurs. Cela dit, où est-il dit dans cualquier bash manpage que le [ o test built-in testerait pour existence d'un fichier de l'argument par défaut (par opposition à -e ) ? Cela ne serait-il pas ambigu ? AFAIK (et AIUI la section "EXPRESSIONS CONDITIONNELLES") la seule chose qui est testée avec votre approche est que l'argument n'est pas vide (ou indéfini), ce qui est, dans ce cas, une tautologie (let $DIR = '' y $FILE = '' alors l'argument est toujours '//' ).

1 votes

Preuve : ls /foo , résultat ls: cannot access /foo: No such file or directory . [ /foo ] && echo 42 , résultat 42 . GNU bash, version 4.2.37(1)-release (i486-pc-linux-gnu).

1 votes

@PointedEars : J'ai omis de spécifier l' -f option, au moment où j'ai écrit cette réponse. Évidemment, vous pouvez toujours utiliser -e si vous n'êtes pas sûr, ce sera un fichier normal. De plus, dans tous mes scripts je cite ces constructions, je dois avoir juste soumis ceci sans vérification adéquate.

58voto

Saiyam Doshi Points 130

Pour tester l'existence du fichier, le paramètre peut être l'un des suivants :

-e: Returns true if file exists (regular file, directory, or symlink)
-f: Returns true if file exists and is a regular file
-d: Returns true if file exists and is a directory
-h: Returns true if file exists and is a symlink

Tous les tests ci-dessous s'appliquent aux fichiers, répertoires et liens symboliques ordinaires :

-r: Returns true if file exists and is readable
-w: Returns true if file exists and is writable
-x: Returns true if file exists and is executable
-s: Returns true if file exists and has a size > 0

Exemple script :

#!/bin/bash
FILE=$1

if [ -f "$FILE" ]; then
   echo "File $FILE exists"
else
   echo "File $FILE does not exist"
fi

44voto

Jahid Points 12756

Vous pouvez le faire :

[[ ! -f "$FILE" ]] && echo "File doesn't exist"

o

if [[ ! -f "$FILE" ]]; then
    echo "File doesn't exist"
fi

Si vous voulez vérifier à la fois le fichier et le dossier, utilisez alors -e au lieu de l'option -f . -e renvoie vrai pour les fichiers ordinaires, les répertoires, les sockets, les fichiers spéciaux de caractères, les fichiers spéciaux de blocs, etc.

2 votes

Ce n'est pas spécifique à bash. La syntaxe provient du shell Korn et est également disponible dans zsh et bash. Elle présente des avantages limités par rapport à la syntaxe standard [ utilité ici cependant.

0 votes

régulier (comme vérifié par -f ) et répertoires ne sont que deux des nombreux types de fichiers . Il y a aussi les sockets, les symlinks, les devices, les fifos, les portes... [ -e testera l'existence de fichiers (de n'importe quel type, y compris régulier, fifo, répertoire...) après la résolution des liens symboliques .

0 votes

@StephaneChazelas : Je ne vois aucune balise ksh ou zsh nulle part. Nous parlons de bash ou de quelque chose qui est standardisé sur la plupart des systèmes unix. Donc, dans le contexte, c'est spécifique à bash. L'OP n'a pas besoin de connaître tous les shells qui existent :D

38voto

artdanil Points 1589

Vous devriez faire attention à ne pas courir test pour une variable non citée, car cela pourrait produire des résultats inattendus :

$ [ -f ]
$ echo $?
0
$ [ -f "" ]
$ echo $?
1

Il est généralement recommandé de placer la variable testée entre guillemets :

#!/bin/sh
FILE=$1

if [ ! -f "$FILE" ]
then
   echo "File $FILE does not exist."
fi

8 votes

La recommandation est d'avoir cada variable entre guillemets, à moins que vous ne sachiez exactement que vous êtes dans l'un des rares cas où elle est inutile, ou dans l'un des cas encore plus rares où elle est nuisible. (Et non, ce n'est pas l'un d'entre eux).

0 votes

Voulez-vous expliquer pourquoi ce n'est pas le cas d'utiliser des guillemets ? Sinon, je ne vois pas l'utilité de ce commentaire.

4 votes

Je voulais dire : Ce n'est pas un des rares cas où c'est inutile ou nuisible. Un programmeur shell devrait s'habituer à mettre (presque) toutes les variables entre guillemets ; cette règle n'est pas limitée aux variables suivantes [ ... ] .

26voto

Stephane Chazelas Points 1418

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X