Comment vérifier si un fichier existe dans Go?

436

La bibliothèque standard de Go n'a pas de fonction destinée uniquement à vérifier si un fichier existe ou non (comme Python os.path.exists). Quelle est la façon idiomatique de le faire?

Sridhar Ratnakumar
la source
Je ne comprends pas vraiment. À la même minute, vous dites qu'il n'y a pas de fonction standard et vous écrivez une réponse avec la fonction standard. Qu'est-ce que je rate ? La question ne devrait-elle pas au moins être résolue?
Denys Séguret
@dystroy - correction de la question.
Sridhar Ratnakumar
11
Il vaut mieux éviter de rechercher l'existence d'un fichier. En raison de la nature racée de la réponse, les informations obtenues indiquent en fait que rien d'utile au-dessus du fichier n'existait dans le temps demandé - mais il peut ne plus exister. La méthode recommandée consiste à simplement ouvrir un fichier et à vérifier s'il échoue ou non.
zzzz
2
Cela a déjà été répondu ici
Sergey Koulikov
2
@zzzz (je sais que ça fait des années, ce commentaire s'adresse aux nouveaux lecteurs) Je suis d'accord dans le cas général. Mais mon application charge une bibliothèque tierce qui prend un chemin de fichier en tant que données d'initialisation, mais segfaults si le fichier n'existe pas. Je pense que c'est un scénario valide pour vérifier si le fichier existe sans essayer de l'ouvrir pour pouvoir signaler l'erreur sans incident fatal, car mon code n'a pas besoin de lire le contenu du fichier ou d'écrire directement dans le fichier.
Sergio Acosta

Réponses:

693

Pour vérifier si un fichier n'existe pas, équivalent à Python if not os.path.exists(filename):

if _, err := os.Stat("/path/to/whatever"); os.IsNotExist(err) {
  // path/to/whatever does not exist
}

Pour vérifier si un fichier existe, équivalent à Python if os.path.exists(filename):

Modifié: selon les commentaires récents

if _, err := os.Stat("/path/to/whatever"); err == nil {
  // path/to/whatever exists

} else if os.IsNotExist(err) {
  // path/to/whatever does *not* exist

} else {
  // Schrodinger: file may or may not exist. See err for details.

  // Therefore, do *NOT* use !os.IsNotExist(err) to test for file existence


}
Sridhar Ratnakumar
la source
3
parfois, il retourne ENOTDIR au lieu de NOTEXIST, par exemple, s'il /etc/bashrcexiste, le /etc/bashrc/foobarretourneENOTDIR
lidaobing
43
Le deuxième extrait est plus subtilement faux; la condition devrait être !os.IsNotExist(err). Il est possible que le fichier existe mais os.Statéchoue pour d'autres raisons (par exemple, autorisation, disque défaillant). L'utilisation err == nilcomme condition catégorise de manière incorrecte ces échecs comme «le fichier n'existe pas».
sqweek
9
Pour vérifier si un fichier existe est incorrect: err est nul si le fichier existe
tangxinfa
1
Assurez-vous de développer ~ sinon il renverra false ... stackoverflow.com/questions/17609732/…
Marcello de Sales
Vous pouvez utiliser os.IsExist () selon le cas, pourrait être plus idiomatique au lieu de faire une double négation en faisant! Os.IsNotExistant ()
Ariel Monaco
126

Réponse de Caleb Spare publiée dans la liste de diffusion des gonuts .

[...] Ce n'est pas vraiment nécessaire très souvent et [...] l'utilisation os.Statest assez facile pour les cas où cela est nécessaire.

[...] Par exemple: si vous allez ouvrir le fichier, il n'y a aucune raison de vérifier s'il existe en premier. Le fichier peut disparaître entre la vérification et l'ouverture, et de toute façon vous devrez vérifier l' os.Openerreur malgré tout. Il vous suffit donc d'appeler os.IsNotExist(err)après avoir essayé d'ouvrir le fichier et de gérer sa non-existence (si cela nécessite une gestion particulière).

[...] Vous n'avez pas besoin de vérifier les chemins existants (et vous ne devriez pas).

  • os.MkdirAllfonctionne que les chemins existent déjà ou non. (Vous devez également vérifier l'erreur de cet appel.)

  • Au lieu d'utiliser os.Create, vous devez utiliser os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666). De cette façon, vous obtiendrez une erreur si le fichier existe déjà. De plus, cela n'a pas de condition de concurrence avec autre chose qui crée le fichier, contrairement à votre version qui vérifie l'existence au préalable.

Tiré de: https://groups.google.com/forum/#!msg/golang-nuts/Ayx-BMNdMFo/4rL8FFHr8v4J

OscarRyz
la source
31

Vous devez utiliser les fonctions os.Stat()et os.IsNotExist()comme dans l'exemple suivant:

// Exists reports whether the named file or directory exists.
func Exists(name string) bool {
    if _, err := os.Stat(name); err != nil {
        if os.IsNotExist(err) {
            return false
        }
    }
    return true
}

L'exemple est extrait d' ici .

Afriza N. Arief
la source
12
Attention: comme l'a souligné stackoverflow.com/a/22467409/712014 , ce code retourne vrai, même si le fichier n'existe pas, par exemple lorsque Stat () renvoie une autorisation refusée.
Michael
19

L' exemple de user11617 est incorrect; il signalera que le fichier existe même dans les cas où il n'existe pas, mais il y a eu une erreur d'une autre sorte.

La signature doit être Exists (chaîne) (bool, erreur). Et puis, en l'occurrence, les sites d'appel ne sont pas meilleurs.

Le code qu'il a écrit serait mieux:

func Exists(name string) bool {
    _, err := os.Stat(name)
    return !os.IsNotExist(err)
}

Mais je suggère ceci à la place:

func Exists(name string) (bool, error) {
  _, err := os.Stat(name)
  if os.IsNotExist(err) {
    return false, nil
  }
  return err != nil, err
}
user3431012
la source
7
Qu'est-ce que l'exemple 5? Pourriez-vous être précis s'il vous plaît.
xlm
1
Votre deuxième exemple doit détruire plusieurs valeurs de retour - par exemple _, err: = os.Stat (nom)
David Duncan
6
Pourquoi revenir err != nilau lieu de err == nil? S'il y a une erreur, alors le fichier n'existe probablement pas?
idbrii
14

Ce que d'autres réponses ont manqué, c'est que le chemin donné à la fonction pourrait en fait être un répertoire. La fonction suivante s'assure que le chemin est vraiment un fichier.

func fileExists(filename string) bool {
    info, err := os.Stat(filename)
    if os.IsNotExist(err) {
        return false
    }
    return !info.IsDir()
}

Une autre chose à souligner: ce code peut toujours conduire à une condition de concurrence critique, où un autre thread ou processus supprime ou crée le fichier spécifié, tandis que la fonction fileExists est en cours d'exécution.

Si cela vous inquiète, utilisez un verrou dans vos threads, sérialisez l'accès à cette fonction ou utilisez un sémaphore interprocessus si plusieurs applications sont impliquées. Si d'autres applications sont impliquées, hors de votre contrôle, vous n'avez pas de chance, je suppose.

ZuBsPaCe
la source
12
    _, err := os.Stat(file)
    if err == nil {
        log.Printf("file %s exists", file)
    } else if os.IsNotExist(err) {
        log.Printf("file %s not exists", file)
    } else {
        log.Printf("file %s stat error: %v", file, err)
    }
tangxinfa
la source
7

L'exemple de fonction:

func file_is_exists(f string) bool {
    _, err := os.Stat(f)
    if os.IsNotExist(err) {
        return false
    }
    return err == nil
}
honmaple
la source
1
N'est-ce pas redondant?
Ilia Choly du
6

Examinons d'abord quelques aspects, à la fois la fonction fournie par le ospackage de golangne sont pas des utilitaires mais des vérificateurs d'erreurs, ce que je veux dire par là, c'est qu'ils ne sont qu'un wrapper pour gérer les erreurs sur plusieurs plates-formes.

Donc, fondamentalement, si os.Statcette fonction ne donne aucune erreur, cela signifie que le fichier existe, si c'est le cas, vous devez vérifier de quel type d'erreur il s'agit, voici l'utilisation de ces deux fonctions os.IsNotExistet os.IsExist.

Cela peut être compris comme l' Staterreur de lancement de fichier car elle n'existe pas ou est-ce une erreur de lancement car elle existe et qu'il y a un problème avec elle.

Le paramètre pris par ces fonctions est de type error, bien que vous puissiez y passer nil, mais cela n'aurait aucun sens.

Cela souligne également le fait que IsExist is not same as !IsNotExistce sont deux choses différentes.

Alors maintenant, si vous voulez savoir si un fichier donné existe, je préférerais que la meilleure façon soit:

if _, err := os.Stat(path/to/file); !os.IsNotExist(err){
   //TODO
} 
Farhaan Bukhsh
la source
1

Comme mentionné dans d'autres réponses, il est possible de construire le comportement / les erreurs requis en utilisant différents drapeaux avec os.OpenFile. En fait, os.Createc'est juste un raccourci sensé par défaut pour le faire:

// Create creates or truncates the named file. If the file already exists,
// it is truncated. If the file does not exist, it is created with mode 0666
// (before umask). If successful, methods on the returned File can
// be used for I/O; the associated file descriptor has mode O_RDWR.
// If there is an error, it will be of type *PathError.
func Create(name string) (*File, error) {
    return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666)
}

Vous devez combiner ces indicateurs vous-même pour obtenir le comportement qui vous intéresse:

// Flags to OpenFile wrapping those of the underlying system. Not all
// flags may be implemented on a given system.
const (
    // Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified.
    O_RDONLY int = syscall.O_RDONLY // open the file read-only.
    O_WRONLY int = syscall.O_WRONLY // open the file write-only.
    O_RDWR   int = syscall.O_RDWR   // open the file read-write.
    // The remaining values may be or'ed in to control behavior.
    O_APPEND int = syscall.O_APPEND // append data to the file when writing.
    O_CREATE int = syscall.O_CREAT  // create a new file if none exists.
    O_EXCL   int = syscall.O_EXCL   // used with O_CREATE, file must not exist.
    O_SYNC   int = syscall.O_SYNC   // open for synchronous I/O.
    O_TRUNC  int = syscall.O_TRUNC  // truncate regular writable file when opened.
)

Selon ce que vous choisissez, vous obtiendrez différentes erreurs.

Voici un exemple où je souhaite ouvrir un fichier pour l'écriture, mais je ne tronquerai un fichier existant que si l'utilisateur a dit que c'était OK:

var f *os.File
if truncateWhenExists {
    // O_TRUNC - truncate regular writable file when opened.
    if f, err = os.OpenFile(filepath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644); err != nil {
        log.Fatalln("failed to force-open file, err:", err)
    }
} else {
    // O_EXCL - used with O_CREATE, file must not exist
    if f, err = os.OpenFile(filepath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0644); err != nil {
        log.Fatalln("failed to open file, err:", err) 
   }
}
Sebastian N
la source
0

Meilleure façon de vérifier si le fichier existe:

if _, err := os.Stat("/path/to/file"); err == nil || os.IsExist(err) {
    // your code here if file exists
}
AlSan
la source