Comment savoir si un fichier standard n'existe pas dans Bash?

3267

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 n'existe pas ?

#!/bin/bash

FILE=$1     
if [ $FILE does not exist ]; then
   echo "File $FILE does not exist."
fi
Bill le lézard
la source
187
J'ai trouvé cette liste d'instructions conditionnelles bash très utile.
Saulius Žemaitaitis
9
Étant la personne très paresseuse que je suis, j'aurais généralement utilisé la construction de contournement idiote suivante: if [ -f $FILE ]; then; else; echo "File $FILE does not exist."; fi;Probablement bien que j'ai trouvé cette question à la place et appris à le faire de manière plus appropriée. :)
Alderath
5
Pour être pendentif, vous devez dire "fichier normal", car la plupart des documents UNIX / POSIX se réfèrent de manière générique à tous les types d'entrées de système de fichiers simplement "fichiers", par exemple, un lien symbolique est un type de fichier, tout comme un canal nommé , fichier normal, répertoire, bloc spécial, caractère spécial, socket, etc.
kevinarpe
11
@kevinarpe si vous voulez tester si quelque chose existe, utilisez -e. -f ne ramasse pas les répertoires, les liens symboliques, etc.
Benubird
14
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"et if [ -f $FILE ];->if [ -f "$FILE" ];
kevinarpe

Réponses:

4527

La commande de test ( [ici) a un opérateur logique "non" qui est le point d'exclamation (similaire à de nombreuses autres langues). Essaye ça:

if [ ! -f /tmp/foo.txt ]; then
    echo "File not found!"
fi
John Feminella
la source
208
Plus succinctement: [! -f /tmp/foo.txt] && echo "Fichier introuvable!"
DavidWinterbottom
38
J'ai eu un peu de mal à trouver la bonne syntaxe pour "si l'un des 2 fichiers n'existe pas". Les deux travaux suivants:if [ ! \( -f "f1" -a -f "f2" \) ] ; then echo MISSING; fi if [ ! -f "f1" ] || [ ! -f "f2" ] ; then echo MISSING; fi
mivk
153
@DavidWinterbottom Encore plus succinctement:[ -f /tmp/foo.txt ] || echo "File not found!"
David W.
27
Le paramètre peut être l'un des suivants:-e: Returns true value, if file exists -f: Return true value, if file exists and regular file -r: Return true value, if file exists and is readable -w: Return true value, if file exists and is writable -x: Return true value, if file exists and is executable -d: Return true value, if exists and is a directory
SD.
5
Il y a une asymétrie entre l'utilisation ! -favec &&et l'utilisation -favec ||. Cela a à voir avec le code de sortie renvoyé par la vérification de non / existence. Si vous avez besoin que votre ligne quitte toujours proprement avec le code de sortie 0 (et parfois vous ne voulez pas de cette contrainte), les deux approches ne sont pas interchangeables. Alternativement, utilisez simplement une ifinstruction et vous n'aurez plus à vous soucier du code de sortie de votre contrôle de non / existence.
Acumenus
670

Test de fichier Bash

-b filename- Bloquer le fichier spécial
-c filename- Fichier de caractères spéciaux
-d directoryname- Vérifier l'existence du répertoire
-e filename- Vérifier l'existence du fichier, quel que soit le type (nœud, répertoire, socket, etc.)
-f filename- Vérifier l'existence régulière du fichier et non un répertoire
-G filename- Vérifier si le fichier existe et appartient à ID de groupe effectif
-G filename set-group-id- Vrai si le fichier existe et est défini-group-id
-k filename- Sticky bit
-L filename- Lien symbolique
-O filename- Vrai si le fichier existe et appartient à l'ID utilisateur effectif
-r filename- Vérifiez si le fichier est lisible
-S filename- Vérifiez si le fichier est socket
-s filename- Vérifiez si le fichier est de taille différente de zéro
-u filename- Vérifiez si le fichier set-user-id est défini
-w filename- Vérifiez si le fichier est accessible en écriture
-x filename- Vérifiez si le fichier est exécutable

Comment utiliser:

#!/bin/bash
file=./file
if [ -e "$file" ]; then
    echo "File exists"
else 
    echo "File does not exist"
fi 

Une expression de test peut être annulée à l'aide de l' !opérateur

#!/bin/bash
file=./file
if [ ! -e "$file" ]; then
    echo "File does not exist"
else 
    echo "File exists"
fi 
BlueCacti
la source
1
@ 0x90 Si vous le souhaitez, vous êtes libre de modifier mon message et de l'ajouter à la liste. Je suppose que vous voulez dire: -n String- Vérifiez si la longueur de la chaîne n'est pas nulle. Ou voulez-vous dire file1 -nt file2- Vérifiez si le fichier1 est plus récent que le fichier 2 (vous pouvez également utiliser -ot pour les anciens)
BlueCacti
1
À propos de -n: The unary operator -z tests for a null string, while -n or no operator at all returns True if a string is not empty.~ ibm.com/developerworks/library/l-bash-test/index.html
BlueCacti
1
pourquoi certains n'ont-ils pas ajouté une fonction comme la fonction existe () {⏎ if [-e "$ 1"]; alors l'écho "$ 1 existe" sinon l'écho "$ 1 n'existe pas" fi}
Mz A
291

Vous pouvez annuler une expression avec "!":

#!/bin/bash
FILE=$1

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

La page de manuel appropriée est man testou, de manière équivalente, man [- ou help testou help [pour la commande bash intégrée.

starbeamrainbowlabs
la source
4
En bash , [est un builtin. Donc, les informations pertinentes sont plutôt obtenues par help [... mais cela montre que [c'est un synonyme de la fonction testintégrée, donc les informations pertinentes sont plutôt obtenues par help test. Voir également la section Expression conditionnelle Bash du manuel .
gniourf_gniourf
@gniourf_gniourf: Oui, mais la [commande intégrée bash se comporte de manière très similaire à la [commande externe , donc soit man testou man [vous donnera une bonne idée de son fonctionnement.
Keith Thompson
8
@KeithThompson sauf que le bash builtin [a plus de commutateurs que la commande externe [trouvé sur mon système ... D'une manière générale , je crois qu'il est préférable de lire la documentation spécifique à un outil donné, et non la documentation spécifique à un autre vaguement connexe. Je me trompe peut-être, cependant;)
gniourf_gniourf
134
[[ -f $FILE ]] || printf '%s does not exist!\n' "$FILE"

En outre, il est possible que le fichier soit un lien symbolique rompu ou un fichier non régulier, comme par exemple un socket, un périphérique ou un fifo. Par exemple, pour ajouter une vérification des liens symboliques rompus:

if [[ ! -f $FILE ]]; then
    if [[ -L $FILE ]]; then
        printf '%s is a broken symlink!\n' "$FILE"
    else
        printf '%s does not exist!\n' "$FILE"
    fi
fi
pistolets
la source
9
Puis-je demander pourquoi les deux "[" dans le test? (par exemple [[! -a $ FILE]]). J'ai essayé toutes les options mentionnées sur une boîte Solaris et seule celle-ci fonctionnait, donc reconnaissante, mais pourquoi?
Dimitrios Mistriotis
30
Les crochets doubles sont une extension "moderne"; Par exemple, ils ne feront pas de division de mots (comme pour les noms de fichiers avec des espaces) et fonctionnent toujours pour les chaînes vides: mywiki.wooledge.org/BashFAQ/031
bw1024
7
selon tldp.org/LDP/abs/html/fto.html -a est en effet identique à -e. Il est «obsolète» et son utilisation est déconseillée. Quoi qu'il en soit +1 pour avoir mentionné de vérifier également le lien symbolique cassé
Luca Borrione
4
@dimitrismistriotis two "[" est une extension non portable implémentée (différemment) par zsh & bash; en général, vous devriez l'éviter si possible.
Bonne personne du
7
J'ai voté pour cette raison PARCE QU'elle a utilisé [[. C'est une extension largement utilisée. Si vous savez que vous utilisez bash, il n'y a aucune raison de ne pas l'utiliser. Il est beaucoup moins sujet aux erreurs que [.
Michael Potter
101

Il convient de mentionner que si vous devez exécuter une seule commande, vous pouvez abréger

if [ ! -f "$file" ]; then
    echo "$file"
fi

à

test -f "$file" || echo "$file"

ou

[ -f "$file" ] || echo "$file"
Elazar Leibovich
la source
Merci! Besoin d'une alternative qui n'utilise pas [[]]
Jonathan
69

Je préfère faire le one-liner suivant, au format compatible POSIX shell:

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

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

Pour quelques commandes, comme je le ferais dans un script:

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

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

JM Becker
la source
1
Tout d'abord, les références de variables non cotées sont sujettes aux erreurs. Cela dit, où est-il dit dans une page de manuel bash que le [ou testintégré testerait l' existence de 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), qui est, dans ce cas, une tautologie (let $DIR = ''et $FILE = '', alors l'argument est toujours '//').
PointedEars
1
Preuve:, ls /fooré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).
PointedEars
@PointedEars: Je n'ai pas spécifié l' -foption, au moment où j'ai écrit cette réponse. Évidemment, vous pouvez toujours utiliser -e, si vous n'êtes pas sûr que ce sera un fichier normal. De plus, dans tous mes scripts, je cite ces constructions, je dois juste les avoir soumises sans vérification adéquate.
JM Becker
ACK. Mais vous savez probablement qu'un monoligne ne peut pas résoudre le problème if-else: il [ $condition ] && if_true || if_falseest sujet aux erreurs. Quoi qu'il en soit, je trouve [ ! -f "$file" ] && if_not_existsplus facile à lire et à comprendre que [ -f "$file" ] || if_not_exists.
PointedEars
55

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 standard:

-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 de script:

#!/bin/bash
FILE=$1

if [ -f "$FILE" ]; then
   echo "File $FILE exists"
else
   echo "File $FILE does not exist"
fi
DAKOTA DU SUD.
la source
39

Tu peux le faire:

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

ou

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

Si vous souhaitez vérifier le fichier et le dossier à la fois, utilisez l' -eoption au lieu de -f. -erenvoie vrai pour les fichiers, répertoires, socket, fichiers spéciaux de caractères normaux, fichiers spéciaux de bloc, etc.

Jahid
la source
1
Ce n'est pas spécifique à bash. La syntaxe vient du shell Korn et est également disponible en zsh et bash. Il a cependant un avantage limité sur l' [utilitaire standard .
Stéphane Chazelas du
les fichiers normaux (vérifiés par -f) et les répertoires ne sont que deux types de fichiers différents . Il existe également des sockets, des liens symboliques, des périphériques, des fifos, des portes ... [ -equi testeront l'existence des fichiers (de tout type, y compris regular, fifo, directory ...) après la résolution du lien symbolique .
Stéphane Chazelas du
@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. OP n'a pas besoin de connaître tous les obus qui existent: D
Jahid
C'était un commentaire sur la partie "spécifique à Bash" de votre réponse.
Stephane Chazelas
@StephaneChazelas: Oui, et dans le contexte (bash et sh standard), c'est spécifique à bash. Je ne vois pas l'intérêt de votre deuxième commentaire, c'est totalement hors contexte. OP n'a pas besoin -e, il a besoin -f.
Jahid
35

Vous devez être prudent lors de l'exécution testd'une variable sans guillemets, 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 doubles:

#!/bin/sh
FILE=$1

if [ ! -f "$FILE" ]
then
   echo "File $FILE does not exist."
fi
artdanil
la source
8
La recommandation est d'avoir chaque variable entourée de guillemets doubles, sauf si vous savez exactement que vous avez l'un des rares cas où cela n'est pas nécessaire, ou l'un des cas encore plus rares où c'est dangereux. (Et non, ce n'est pas l'un d'entre eux.)
Uwe
Souhaitez-vous expliquer pourquoi ce n'est pas le cas pour utiliser des guillemets doubles? Sinon, je ne vois pas l'utilité dans le commentaire.
artdanil
4
Je voulais dire: ce n'est pas l'un des rares cas où c'est inutile ou dangereux. Un programmeur shell devrait s'habituer à mettre (presque) chaque variable entre guillemets; cette règle n'est pas limitée à [ ... ].
Uwe
24

Il existe trois façons distinctes de procéder:

  1. Annulez le statut de sortie avec bash (aucune autre réponse ne l'a dit):

    if ! [ -e "$file" ]; then
        echo "file does not exist"
    fi

    Ou:

    ! [ -e "$file" ] && echo "file does not exist"
  2. Annulez le test dans la commande test [(c'est la façon dont la plupart des réponses ont été présentées auparavant):

    if [ ! -e "$file" ]; then
        echo "file does not exist"
    fi

    Ou:

    [ ! -e "$file" ] && echo "file does not exist"
  3. Agir sur le résultat du test négatif ( ||au lieu de &&):

    Seulement:

    [ -e "$file" ] || echo "file does not exist"

    Cela a l'air idiot (IMO), ne l'utilisez pas sauf si votre code doit être portable sur le shell Bourne (comme celui /bin/shde Solaris 10 ou antérieur) qui manquait de l'opérateur de négation du pipeline ( !):

    if [ -e "$file" ]; then
        :
    else
        echo "file does not exist"
    fi
Stéphane Chazelas
la source
Une différence de portabilité entre ! [et [ !?
Frozen Flame
3
Il ! [ s'agit de POSIX pour les pipelines shell 2.9.2 (n'importe quelle commande) Otherwise, the exit status shall be the logical NOT of the exit status of the last command et de [ ! POSIX pour le test. ! expression True if expression is false. False if expression is true. Donc, les deux sont POSIX, et d'après mon expérience, les deux sont largement pris en charge.
1
@FrozenFlame, le shell Bourne n'avait pas le !mot - clé introduit par le shell Korn. Sauf peut-être pour Solaris 10 et les versions antérieures, il est peu probable que vous rencontriez un shell Bourne de nos jours.
Stéphane Chazelas du
23

Dans

[ -f "$file" ]

la [commande effectue un stat()(pas lstat()) appel système sur le chemin stocké dans $fileet renvoie true si cet appel système réussit et que le type de fichier renvoyé par stat()est " normal ".

Donc, si [ -f "$file" ]renvoie true, vous pouvez dire que le fichier existe et qu'il s'agit d'un fichier normal ou d'un lien symbolique qui se résout finalement en un fichier normal (ou du moins c'était au moment de lastat() ).

Cependant, si elle renvoie false (ou si [ ! -f "$file" ]ou ! [ -f "$file" ]retourne true), il existe de nombreuses possibilités différentes:

  • le fichier n'existe pas
  • le fichier existe mais n'est pas un habitué fichier (peut être un périphérique, fifo, répertoire, socket ...)
  • le fichier existe mais vous ne disposez pas des droits de recherche sur le répertoire parent
  • le fichier existe mais ce chemin d'accès est trop long
  • le fichier est un lien symbolique vers un fichier standard, mais vous n'avez pas l'autorisation de recherche sur certains des répertoires impliqués dans la résolution du lien symbolique.
  • ... toute autre raison pour laquelle l' stat()appel système peut échouer.

Bref, ce devrait ê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 savoir avec certitude que le fichier n'existe pas, nous aurions besoin de l' stat()appel système pour retourner avec un code d'erreur de ENOENT( ENOTDIRnous indique que l'un des composants du chemin n'est pas un répertoire est un autre cas où nous pouvons dire que le fichier ne l'est pas exister par ce chemin). Malheureusement,[ commande ne nous le fait pas savoir. Il retournera faux si le code d'erreur est ENOENT, EACCESS (autorisation refusée), ENAMETOOLONG ou autre.

Le [ -e "$file" ]test peut également être effectué avec ls -Ld -- "$file" > /dev/null. Dans ce cas, lsvous dira pourquoi l' stat()échec, bien que les informations ne puissent pas être facilement utilisées par programme:

$ 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 lsme 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. La [commande a simplement ignoré le problème.

Avec le zshshell, vous pouvez interroger le code d'erreur avec la $ERRNOvariable spéciale après la [commande en échec et décoder ce nombre à l'aide du $errnostableau spécial du zsh/systemmodule:

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, le $errnossupport a été rompu avec certaines versions de zshlors de sa construction avec des versions récentes degcc ).

Stéphane Chazelas
la source
11

Pour inverser un test, utilisez "!". Cela équivaut à l'opérateur logique "non" dans d'autres langues. Essaye ça:

if [ ! -f /tmp/foo.txt ];
then
    echo "File not found!"
fi

Ou écrit d'une manière légèrement différente:

if [ ! -f /tmp/foo.txt ]
    then echo "File not found!"
fi

Ou vous pouvez utiliser:

if ! [ -f /tmp/foo.txt ]
    then echo "File not found!"
fi

Ou, en présageant tous ensemble:

if ! [ -f /tmp/foo.txt ]; then echo "File not found!"; fi

Qui peut s'écrire (en utilisant alors l'opérateur "et": &&) comme:

[ ! -f /tmp/foo.txt ] && echo "File not found!"

Qui ressemble plus court à ceci:

[ -f /tmp/foo.txt ] || echo "File not found!"

la source
10

Ce code fonctionne également.

#!/bin/bash
FILE=$1
if [ -f $FILE ]; then
 echo "File '$FILE' Exists"
else
 echo "The File '$FILE' Does Not Exist"
fi
Lemon Kazi
la source
7

La manière la plus simple

FILE=$1
[ ! -e "${FILE}" ] && echo "does not exist" || echo "exists"
lancerex
la source
6

Ce script shell fonctionne également pour trouver un fichier dans un répertoire:

echo "enter file"

read -r a

if [ -s /home/trainee02/"$a" ]
then
    echo "yes. file is there."
else
    echo "sorry. file is not there."
fi
Simmant
la source
3
Il n'est pas clair que cela ajoute quelque chose que d'autres réponses n'ont pas encore donné. Il code en dur un chemin. Étant donné que la question est étiquetée bash , elle peut être utilisée read -p "Enter file name: " -r aaussi bien pour l'invite que pour la lecture. Il utilise des guillemets autour de la variable; c'est bien, mais devrait être expliqué. Il vaudrait peut-être mieux qu'il répète le nom du fichier. Et cela vérifie que le fichier existe et n'est pas vide (c'est le sens de -s) alors que la question porte sur n'importe quel fichier, vide ou non (pour lequel -fest plus approprié).
Jonathan Leffler
4

parfois, il peut être utile d'utiliser && et || les opérateurs.

Comme dans (si vous avez la commande "test"):

test -b $FILE && echo File not there!

ou

test -b $FILE || echo File there!
André
la source
2

Si vous souhaitez utiliser à la testplace de [], vous pouvez utiliser !pour obtenir la négation:

if ! test "$FILE"; then
  echo "does not exist"
fi
Mauro Zallocco
la source
0

Vous pouvez également regrouper plusieurs commandes dans la même ligne

[ -f "filename" ] || ( echo test1 && echo test2 && echo test3 )

ou

[ -f "filename" ] || { echo test1 && echo test2 && echo test3 ;}

Si le nom de fichier ne se ferme pas, la sortie sera

test1
test2
test3

Remarque: (...) s'exécute dans un sous-shell, {...;} s'exécute dans le même shell. La notation entre accolades ne fonctionne qu'en bash.

upteryx
la source
1
Non, la notation entre accolades n'est pas uniquement bash; il fonctionne dans n'importe quel shell compatible POSIX.
Charles Duffy
0
envfile=.env

if [ ! -f "$envfile" ]
then
    echo "$envfile does not exist"
    exit 1
fi
kta
la source
Veuillez fournir une explication avec le code.
Zohar
Bien que ce code puisse résoudre le problème de l'OP, il est préférable d'inclure une explication sur la façon dont votre code résout le problème de l'OP. De cette façon, les futurs visiteurs peuvent apprendre de votre message et l'appliquer à leur propre code. SO n'est pas un service de codage, mais une ressource de connaissances. En outre, les réponses complètes de haute qualité sont plus susceptibles d'être votées positivement. Ces fonctionnalités, ainsi que l'exigence que tous les messages soient autonomes, sont quelques-unes des forces de SO en tant que plate-forme, ce qui le différencie des forums. Vous pouvez modifier pour ajouter des informations supplémentaires et / ou pour compléter vos explications avec la documentation source.
ysf