¿Cómo verificar si existe un archivo en Go?

436

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?

Sridhar Ratnakumar
fuente
Realmente no lo entiendo. En el mismo minuto dice que no hay una función estándar y escribe una respuesta con la función estándar. Qué me estoy perdiendo ? ¿No debería al menos resolverse la pregunta?
Denys Séguret
@distroy - solucionó la pregunta.
Sridhar Ratnakumar
11
Es mejor evitar preguntar sobre la existencia del archivo. B / c de la naturaleza picante de la respuesta, la información obtenida dice que en realidad no existía nada útil sobre el archivo en el tiempo solicitado, pero es posible que ya no exista. La forma recomendable es simplemente abrir un archivo y verificar si falla o no.
zzzz
2
Esto ya ha sido respondido aquí
Sergey Koulikov
2
@zzzz (Sé que han pasado años, este comentario es para nuevos lectores) Estoy de acuerdo en el caso general. Pero mi aplicación carga una biblioteca de terceros que toma alguna ruta de archivo como datos de inicialización, pero se daña si el archivo no existe. Creo que este es un escenario válido para verificar si el archivo existe sin intentar abrirlo para poder informar el error sin un bloqueo fatal, ya que mi código no necesita leer el contenido del archivo o escribir directamente en el archivo.
Sergio Acosta

Respuestas:

693

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


}
Sridhar Ratnakumar
fuente
3
a veces devuelve ENOTDIR en lugar de NOTEXIST, por ejemplo, si /etc/bashrcexiste, /etc/bashrc/foobarregresaráENOTDIR
lidaobing
43
El segundo fragmento es más sutilmente incorrecto; La condición debe ser !os.IsNotExist(err). Es posible que el archivo exista pero os.Statfalle por otros motivos (por ejemplo, permiso, disco defectuoso). El uso err == nilcomo la condición clasifica incorrectamente errores tales como "el archivo no existe".
semana del
99
Para verificar si un archivo existe es incorrecto: err es nulo si el archivo existe
tangxinfa
1
Asegúrese de expandir ~ o de lo contrario devolverá falso ... stackoverflow.com/questions/17609732/…
Marcello de Sales
Podría usar os.IsExist () según el caso, podría ser más idiomático en lugar de hacer una doble negación al hacerlo! Os.IsNotExistant ()
Ariel Monaco
126

Respuesta de Caleb Spare publicada en la lista de correo de gonuts .

[...] En realidad no se necesita con mucha frecuencia y [...] su uso os.States 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.Openerror independientemente. Entonces, simplemente llama os.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.MkdirAllfunciona si las rutas ya existen o no. (También debe verificar el error de esa llamada).

  • En lugar de usar os.Create, debes usar os.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

OscarRyz
fuente
31

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í .

Afriza N. Arief
fuente
12
Cuidado: como apuntó stackoverflow.com/a/22467409/712014 , este código devuelve verdadero, incluso si el archivo no existe, por ejemplo, cuando Stat () devuelve permiso denegado.
Michael
19

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
}
usuario3431012
fuente
77
¿Qué es el ejemplo 5? ¿Podría ser específico por favor?
xlm
1
Su segundo ejemplo necesita desestructurar múltiples valores de retorno, por ejemplo _, err: = os.Stat (name)
David Duncan
66
¿Por qué volver en err != nillugar de err == nil? Si hay un error, ¿entonces el archivo probablemente no existe?
idbrii
14

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.

ZuBsPaCe
fuente
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
fuente
7

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
}
honmaple
fuente
1
¿No es redundante?
Ilia Choly
6

Veamos primero algunos aspectos, tanto la función proporcionada por el ospaquete de golangno 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.Statsi 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.IsNotExisty os.IsExist.

Esto puede entenderse como el Staterror 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 !IsNotExistson 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
} 
Farhaan Bukhsh
fuente
1

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.Createes 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) 
   }
}
Sebastian N
fuente
0

La mejor manera de verificar si el archivo existe:

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