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?
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?
Respostas:
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
}
NOTEXIST
, por exemplo, se /etc/bashrc
existir, o /etc/bashrc/foobar
retornaráENOTDIR
!os.IsNotExist(err)
. É possível que o arquivo exista, mas os.Stat
falhe por outros motivos (por exemplo, permissão, falha no disco). O uso err == nil
como condição categoriza incorretamente falhas como "o arquivo não existe".
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.Open
erro. Então você simplesmente ligaos.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.MkdirAll
funciona 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 usaros.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
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 .
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
}
err != nil
vez de err == nil
? Se houver um erro, o arquivo provavelmente não existe?
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.
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
}
Vejamos alguns aspectos primeiro: ambas as funções fornecidas pelo os
pacote golang
nã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.Stat
essa 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.IsNotExist
e os.IsExist
.
Isso pode ser entendido como o Stat
erro 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 nil
para ele, mas não faria sentido.
Isso também aponta para o fato de que IsExist is not same as !IsNotExist
sã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
}
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)
}
}