Como verificar se existe um arquivo no Go?

435

A biblioteca padrão da Go não possui uma função destinada apenas a verificar se um arquivo existe ou não (como o Python os.path.exists). Qual é a maneira idiomática de fazer isso?

Sridhar Ratnakumar
fonte
Eu realmente não entendo. No mesmo minuto, você diz que não há função padrão e escreve uma resposta com a função padrão. O que estou perdendo ? Pelo menos a questão não deveria ser resolvida?
Denys Séguret
@dystroy - corrigiu a questão.
Sridhar Ratnakumar
11
É melhor evitar questionar a existência de arquivos. B / c da natureza atrevida da resposta, as informações obtidas dizem que, na verdade, nada útil acima do arquivo existia no tempo solicitado - mas pode não existir mais. A maneira recomendável é simplesmente abrir um arquivo e verificar se isso falha ou não.
Zzzz
2
Isso já foi respondido aqui
Sergey Koulikov
2
@zzzz (eu sei que faz anos, esse comentário é para novos leitores) Eu concordo no caso geral. Mas meu aplicativo carrega uma biblioteca de terceiros que usa algum caminho do arquivo como dados de inicialização, mas segfaults se o arquivo não existir. Eu acho que esse é um cenário válido para verificar se o arquivo existe sem tentar abri-lo para poder relatar o erro sem uma falha fatal, pois meu código não precisa ler o conteúdo nem gravar diretamente no arquivo.
Sergio Acosta

Respostas:

690

Para verificar se um arquivo não existe, equivalente ao 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 se existe um arquivo, equivalente ao do Python if os.path.exists(filename):

Editado: por comentários recentes

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
fonte
3
às vezes ele retornar ENOTDIR em vez de NOTEXIST, por exemplo, se /etc/bashrcexistir, o /etc/bashrc/foobarretornaráENOTDIR
lidaobing
43
O segundo trecho está mais sutilmente errado; a condição deve ser !os.IsNotExist(err). É possível que o arquivo exista, mas os.Statfalhe por outros motivos (por exemplo, permissão, falha no disco). O uso err == nilcomo condição categoriza incorretamente falhas como "o arquivo não existe".
Sqweek
9
Para verificar se existe um arquivo está errado: err é nulo se o arquivo existe
tangxinfa
1
Certifique-se de expandir ~ ou então ele retornará false ... stackoverflow.com/questions/17609732/…
Marcello de Sales
Você poderia usar os.IsExist (), dependendo do caso, poderia ser mais idioma em vez de fazer uma dupla negação ao fazer isso! Os.IsNotExistant ()
Ariel Monaco
126

Responder por Caleb Spare publicado na lista de discussão gonuts .

[...] Na verdade, não é necessário com muita freqüência e [...] o uso os.Staté fácil o suficiente para os casos em que é necessário.

[...] Por exemplo: se você deseja abrir o arquivo, não há motivo para verificar se ele existe primeiro. O arquivo pode desaparecer entre a verificação e a abertura e, mesmo assim, você precisará verificar o os.Openerro. Então você simplesmente liga os.IsNotExist(err)depois de tentar abrir o arquivo e lida com a sua inexistência (se isso exigir tratamento especial).

[...] Você não precisa verificar os caminhos existentes (e não deveria).

  • os.MkdirAllfunciona se os caminhos já existem ou não. (Também é necessário verificar o erro dessa chamada.)

  • Em vez de usar os.Create, você deve usar os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666). Dessa forma, você receberá um erro se o arquivo já existir. Além disso, isso não tem uma condição de corrida com outra coisa criando o arquivo, diferente da sua versão que verifica a existência de antemão.

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

OscarRyz
fonte
30

Você deve usar as funções os.Stat()e os.IsNotExist()como no exemplo a seguir:

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

O exemplo é extraído daqui .

Afriza N. Arief
fonte
12
Cuidado: como salientou stackoverflow.com/a/22467409/712014 , esse código retorna verdadeiro, mesmo que o arquivo não exista, por exemplo, quando Stat () retorna permissão negada.
Michael
19

O exemplo do usuário11617 está incorreto; relatará que o arquivo existe mesmo nos casos em que não existe, mas houve um erro de outro tipo.

A assinatura deve ser Existe (string) (bool, erro). E então, por acaso, os sites de chamadas não são melhores.

O código que ele escreveu seria melhor como:

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

Mas sugiro isso:

func Exists(name string) (bool, error) {
  _, err := os.Stat(name)
  if os.IsNotExist(err) {
    return false, nil
  }
  return err != nil, err
}
user3431012
fonte
7
Qual é o exemplo 5? Você poderia ser específico, por favor.
Xlm 17/03/14
1
Seu segundo exemplo necessidades para desestruturar vários valores de retorno - por exemplo _, err: = os.stat (nome)
David Duncan
6
Por que retornar em err != nilvez de err == nil? Se houver um erro, o arquivo provavelmente não existe?
Idbrii 8/03/19
14

O que outras respostas perderam, é que o caminho fornecido para a função pode realmente ser um diretório. A função a seguir garante que o caminho seja realmente um arquivo.

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

Outra coisa a destacar: esse código ainda pode levar a uma condição de corrida, em que outro thread ou processo exclui ou cria o arquivo especificado, enquanto a função fileExists está em execução.

Se você estiver preocupado com isso, use um bloqueio em seus threads, serialize o acesso a essa função ou use um semáforo entre processos, se vários aplicativos estiverem envolvidos. Se outras aplicações estiverem envolvidas, fora do seu controle, você estará sem sorte, eu acho.

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

O exemplo da função:

func file_is_exists(f string) bool {
    _, err := os.Stat(f)
    if os.IsNotExist(err) {
        return false
    }
    return err == nil
}
honmaple
fonte
1
Não é o caso redundante?
Ilia Choly
6

Vejamos alguns aspectos primeiro: ambas as funções fornecidas pelo ospacote golangnão são utilitários, mas verificadores de erros. O que quero dizer com isso é que eles são apenas um invólucro para lidar com erros em plataformas cruzadas.

Então, basicamente, se os.Statessa função não der nenhum erro, significa que o arquivo existe, se você precisar verificar que tipo de erro é, aqui vem o uso dessas duas funções os.IsNotExiste os.IsExist.

Isso pode ser entendido como o Staterro de lançamento do arquivo porque ele não existe ou é um erro de lançamento porque existe e há algum problema com ele.

O parâmetro que essas funções assumem é do tipo error, embora você possa passar nilpara ele, mas não faria sentido.

Isso também aponta para o fato de que IsExist is not same as !IsNotExistsão duas coisas diferentes.

Portanto, agora, se você quiser saber se um determinado arquivo existe, prefiro a melhor maneira:

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

Como mencionado em outras respostas, é possível construir o comportamento / erros necessários usando diferentes sinalizadores os.OpenFile. De fato, os.Createé apenas uma abreviação de padrões sensíveis para fazer isso:

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

Você mesmo deve combinar essas bandeiras para obter o comportamento em que está interessado:

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

Dependendo do que você escolher, você receberá erros diferentes.

Aqui está um exemplo em que desejo abrir um arquivo para gravação, mas somente truncarei um arquivo existente se o usuário disser que está 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
fonte
0

Melhor maneira de verificar se o arquivo existe:

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