La biblioteca estándar de Go no tiene una función destinada únicamente a verificar si un archivo existe o no (como Python os.path.exists
). ¿Cuál es la forma idiomática de hacerlo?
La biblioteca estándar de Go no tiene una función destinada únicamente a verificar si un archivo existe o no (como Python os.path.exists
). ¿Cuál es la forma idiomática de hacerlo?
Respuestas:
Para verificar si un archivo no existe, equivalente a Python if not os.path.exists(filename)
:
if _, err := os.Stat("/path/to/whatever"); os.IsNotExist(err) {
// path/to/whatever does not exist
}
Para verificar si existe un archivo, equivalente al de Python if os.path.exists(filename)
:
Editado: por comentarios recientes
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
, por ejemplo, si /etc/bashrc
existe, /etc/bashrc/foobar
regresaráENOTDIR
!os.IsNotExist(err)
. Es posible que el archivo exista pero os.Stat
falle por otros motivos (por ejemplo, permiso, disco defectuoso). El uso err == nil
como la condición clasifica incorrectamente errores tales como "el archivo no existe".
Respuesta de Caleb Spare publicada en la lista de correo de gonuts .
[...] En realidad no se necesita con mucha frecuencia y [...] su uso
os.Stat
es lo suficientemente fácil para los casos en que se requiere.[...] Por ejemplo: si va a abrir el archivo, no hay razón para verificar si existe primero. El archivo podría desaparecer entre la comprobación y la apertura, y de todos modos deberá verificar el
os.Open
error independientemente. Entonces, simplemente llamaos.IsNotExist(err)
después de intentar abrir el archivo y trata su inexistencia allí (si eso requiere un manejo especial).[...] No es necesario que compruebe las rutas existentes (y no debería).
os.MkdirAll
funciona si las rutas ya existen o no. (También debe verificar el error de esa llamada).En lugar de usar
os.Create
, debes usaros.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
. De esa forma, obtendrá un error si el archivo ya existe. Además, esto no tiene una condición de carrera con algo más que hace el archivo, a diferencia de su versión que verifica la existencia de antemano.
Tomado de: https://groups.google.com/forum/#!msg/golang-nuts/Ayx-BMNdMFo/4rL8FFHr8v4J
Debe usar las funciones os.Stat()
y os.IsNotExist()
como en el siguiente ejemplo:
// 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
}
El ejemplo se extrae de aquí .
El ejemplo de user11617 es incorrecto; informará que el archivo existe incluso en los casos en que no existe, pero hubo un error de algún otro tipo.
La firma debe ser Exists (string) (bool, error). Y luego, como sucede, los sitios de llamadas no son mejores.
El código que escribió sería mejor como:
func Exists(name string) bool {
_, err := os.Stat(name)
return !os.IsNotExist(err)
}
Pero sugiero esto en su lugar:
func Exists(name string) (bool, error) {
_, err := os.Stat(name)
if os.IsNotExist(err) {
return false, nil
}
return err != nil, err
}
err != nil
lugar de err == nil
? Si hay un error, ¿entonces el archivo probablemente no existe?
Lo que otras respuestas perdieron es que la ruta dada a la función podría ser un directorio. La siguiente función se asegura de que la ruta sea realmente un archivo.
func fileExists(filename string) bool {
info, err := os.Stat(filename)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}
Otra cosa a destacar: este código aún podría conducir a una condición de carrera, donde otro subproceso o proceso elimina o crea el archivo especificado, mientras se ejecuta la función fileExists.
Si le preocupa esto, use un bloqueo en sus hilos, serialice el acceso a esta función o use un semáforo entre procesos si hay varias aplicaciones involucradas. Si hay otras aplicaciones involucradas, fuera de tu control, supongo que no tienes suerte.
El ejemplo de la función:
func file_is_exists(f string) bool {
_, err := os.Stat(f)
if os.IsNotExist(err) {
return false
}
return err == nil
}
Veamos primero algunos aspectos, tanto la función proporcionada por el os
paquete de golang
no son utilidades sino verificadores de errores, lo que quiero decir con eso es que son solo un contenedor para manejar errores en la plataforma cruzada.
Básicamente, si os.Stat
si esta función no produce ningún error, eso significa que el archivo existe si es necesario, debe verificar qué tipo de error es, aquí viene el uso de estas dos funciones os.IsNotExist
y os.IsExist
.
Esto puede entenderse como el Stat
error de lanzamiento del archivo porque no existe o es un error de lanzamiento porque existe y hay algún problema con él.
El parámetro que toman estas funciones es de tipo error
, aunque es posible que pueda pasarlo nil
, pero no tendría sentido.
Esto también apunta al hecho de que IsExist is not same as !IsNotExist
son dos cosas diferentes.
Entonces, si desea saber si existe un archivo determinado en go, preferiría que la mejor manera sea:
if _, err := os.Stat(path/to/file); !os.IsNotExist(err){
//TODO
}
Como se menciona en otras respuestas, es posible construir los comportamientos / errores requeridos a partir del uso de diferentes indicadores con os.OpenFile
. De hecho, os.Create
es solo una abreviatura de valores razonables para hacerlo:
// 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)
}
Debe combinar estas banderas usted mismo para obtener el comportamiento que le interesa:
// 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.
)
Dependiendo de lo que elija, obtendrá diferentes errores.
Aquí hay un ejemplo donde deseo abrir un archivo para escribir, pero solo truncaré un archivo existente si el usuario ha dicho que está bien:
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)
}
}