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?
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?
Risposte:
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
}
NOTEXIST
, ad esempio, se /etc/bashrc
esiste, /etc/bashrc/foobar
torneràENOTDIR
!os.IsNotExist(err)
. È possibile che il file esista ma os.Stat
non riesca per altri motivi (ad es. Permesso, disco guasto). L'uso err == nil
come condizione classifica erroneamente tali errori come "il file non esiste".
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.Open
errore indipendentemente. Quindi semplicemente chiamios.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.MkdirAll
funziona indipendentemente dal fatto che i percorsi esistano già. (Inoltre è necessario controllare l'errore da quella chiamata.)Invece di usare
os.Create
, dovresti usareos.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
È 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 .
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
}
err != nil
invece di err == nil
? Se c'è un errore, probabilmente il file non esiste?
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.
L'esempio di funzione:
func file_is_exists(f string) bool {
_, err := os.Stat(f)
if os.IsNotExist(err) {
return false
}
return err == nil
}
Diamo un'occhiata ad alcuni aspetti in primo luogo, sia la funzione fornita dal os
pacchetto golang
non sono utility ma controlli errori, cosa intendo dire che sono solo un wrapper per gestire gli errori su più piattaforme.
Quindi, fondamentalmente, se os.Stat
questa 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.IsNotExist
e os.IsExist
.
Questo può essere inteso come l' Stat
errore 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 nil
ma non avrebbe senso.
Ciò indica anche IsExist is not same as !IsNotExist
che 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
}
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)
}
}