Come verificare se esiste un file in Go?


436

La libreria standard di Go non ha una funzione destinata esclusivamente a verificare se esiste un file o meno (come quello di Python os.path.exists). Qual è il modo idiomatico per farlo?


Non capisco davvero. Nello stesso minuto dici che non esiste una funzione standard e scrivi una risposta con la funzione standard. Cosa mi sto perdendo? Almeno la domanda non dovrebbe essere risolta?
Denys Séguret,

@dystroy - risolta la domanda.
Sridhar Ratnakumar,

11
È meglio evitare di indagare sull'esistenza dei file. B / c della natura audace della risposta, le informazioni ottenute dicono che in realtà non esisteva nulla di utile al di sopra del file nel tempo richiesto, ma potrebbe non esistere più. Il modo raccomandabile è semplicemente aprire un file e verificare se fallisce o no.
zzzz,

2
È già stata data una risposta qui
Sergey Koulikov l'

2
@zzzz (so che sono passati anni, questo commento è per i nuovi lettori) Sono d'accordo nel caso generale. Ma la mia app carica una libreria di terze parti che accetta alcuni percorsi di file come dati di inizializzazione ma segfault se il file non esiste. Penso che questo sia uno scenario valido per verificare se il file esiste senza tentare di aprirlo per poter segnalare l'errore senza un arresto irreversibile, poiché il mio codice non ha bisogno di leggere il contenuto del file o scrivere direttamente sul file.
Sergio Acosta,

Risposte:


693

Per verificare se un file non esiste, equivalente a quello di Python if not os.path.exists(filename):

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

Per verificare se esiste un file, equivalente a quello di Python if os.path.exists(filename):

Modificato: per commenti recenti

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


}

3
a volte restituisce ENOTDIR invece che NOTEXIST, ad esempio, se /etc/bashrcesiste, /etc/bashrc/foobartorneràENOTDIR
lidaobing

43
Il secondo frammento è più sottilmente sbagliato; la condizione dovrebbe essere !os.IsNotExist(err). È possibile che il file esista ma os.Statnon riesca per altri motivi (ad es. Permesso, disco guasto). L'uso err == nilcome condizione classifica erroneamente tali errori come "il file non esiste".
sqweek,

9
Per verificare se un file esiste è errato: err è nullo se il file esiste
tangxinfa

1
Assicurati di espandere ~ altrimenti restituirà false ... stackoverflow.com/questions/17609732/…
Marcello de Sales

Potresti usare os.IsExist () a seconda del caso, potrebbe essere più idiomatico invece di fare una doppia negazione quando lo fai! Os.IsNotExistant ()
Ariel Monaco

126

Risposta di Caleb Spare pubblicata nella mailing list di gonuts .

[...] In realtà non è necessario molto spesso e [...] l'uso os.Statè abbastanza facile per i casi in cui è richiesto.

[...] Ad esempio: se hai intenzione di aprire il file, non c'è motivo di verificare se esiste prima. Il file potrebbe scomparire tra il controllo e l'apertura e comunque dovrai controllare l' os.Openerrore indipendentemente. Quindi semplicemente chiami os.IsNotExist(err)dopo aver provato ad aprire il file e gestisci la sua inesistenza lì (se ciò richiede una gestione speciale).

[...] Non è necessario controllare i percorsi esistenti (e non dovresti).

  • os.MkdirAllfunziona indipendentemente dal fatto che i percorsi esistano già. (Inoltre è necessario controllare l'errore da quella chiamata.)

  • Invece di usare os.Create, dovresti usare os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666). In questo modo riceverai un errore se il file esiste già. Inoltre, questo non ha una condizione di competizione con qualcos'altro che crea il file, a differenza della tua versione che verifica in anticipo l'esistenza.

Tratto da: https://groups.google.com/forum/#!msg/golang-nuts/Ayx-BMNdMFo/4rL8FFHr8v4J


31

È necessario utilizzare le funzioni os.Stat()e os.IsNotExist()come nell'esempio seguente:

// 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'esempio è estratto da qui .


12
Attenzione: come ha sottolineato stackoverflow.com/a/22467409/712014 , questo codice restituisce true, anche se il file non esiste, ad esempio quando Stat () restituisce l'autorizzazione negata.
Michael,

19

L' esempio di user11617 non è corretto; segnalerà che il file esiste anche nei casi in cui non esiste, ma si è verificato un errore di qualche altro tipo.

La firma dovrebbe essere Exists (stringa) (bool, errore). E poi, come accade, i siti di chiamata non sono migliori.

Il codice che ha scritto sarebbe meglio come:

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

Ma suggerisco questo invece:

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

7
Cos'è l'esempio 5? Potresti essere specifico per favore.
XL

1
Il tuo secondo esempio deve distruggere più valori di ritorno - ad esempio _, err: = os.Stat (nome)
David Duncan,

6
Perché tornare err != nilinvece di err == nil? Se c'è un errore, probabilmente il file non esiste?
idbrii

14

Ciò che mancava ad altre risposte è che il percorso fornito alla funzione potrebbe effettivamente essere una directory. La seguente funzione assicura che il percorso sia realmente un file.

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

Un'altra cosa da sottolineare: questo codice potrebbe comunque portare a una condizione di competizione, in cui un altro thread o processo elimina o crea il file specificato, mentre la funzione fileExists è in esecuzione.

Se sei preoccupato per questo, usa un lucchetto nei thread, serializza l'accesso a questa funzione o usa un semaforo tra processi se sono coinvolte più applicazioni. Se sono coinvolte altre applicazioni, al di fuori del tuo controllo, sei sfortunato, immagino.


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

7

L'esempio di funzione:

func file_is_exists(f string) bool {
    _, err := os.Stat(f)
    if os.IsNotExist(err) {
        return false
    }
    return err == nil
}

1
Il se non è ridondante?
Ilia Choly,

6

Diamo un'occhiata ad alcuni aspetti in primo luogo, sia la funzione fornita dal ospacchetto golangnon sono utility ma controlli errori, cosa intendo dire che sono solo un wrapper per gestire gli errori su più piattaforme.

Quindi, fondamentalmente, se os.Statquesta funzione non fornisce alcun errore, ciò significa che il file esiste se è necessario controllare che tipo di errore è, ecco che arriva l'uso di queste due funzioni os.IsNotExiste os.IsExist.

Questo può essere inteso come l' Staterrore di lancio del file perché non esiste o sta generando un errore perché esiste e c'è qualche problema con esso.

Il parametro utilizzato da queste funzioni è di tipo error, anche se potresti essere in grado di passarci nilma non avrebbe senso.

Ciò indica anche IsExist is not same as !IsNotExistche sono due cose diverse.

Quindi ora se vuoi sapere se un determinato file esiste in go, preferirei che il modo migliore sia:

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

1

Come menzionato in altre risposte, è possibile costruire il comportamento / gli errori richiesti utilizzando diversi flag con os.OpenFile. In effetti, os.Createè solo una scorciatoia di default sensata per farlo:

// 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)
}

Dovresti combinare queste bandiere tu stesso per ottenere il comportamento che ti interessa:

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

A seconda di ciò che scegli, otterrai diversi errori.

Ecco un esempio in cui desidero aprire un file per la scrittura, ma troncerò un file esistente solo se l'utente ha detto che è 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) 
   }
}

0

Il modo migliore per verificare l'esistenza del file:

if _, err := os.Stat("/path/to/file"); err == nil || os.IsExist(err) {
    // your code here if file exists
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.