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?


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


}

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


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 .


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
}

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.


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

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
}

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
} 

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

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
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.