658

Go's standard library does not have a function solely intended to check if a file exists or not (like Python's os.path.exists). What is the idiomatic way to do it?

Sridhar Ratnakumar
  • 81,433
  • 63
  • 146
  • 187
  • 2
    I don't really get it. At the same minute you say there is no standard function and you write an answer with the standard function. What am I missing ? Shouldn't at least the question be fixed ? – Denys Séguret Sep 21 '12 at 07:03
  • 19
    One should better avoid inquiring file existence. B/c of the racy nature of the answer, the obtained information says actually nothing useful above the file existed in the time asked - but it may not exist anymore. The recommendable way is to simply open a file and check if that fails or not. – zzzz Sep 26 '12 at 07:56
  • 4
    @zzzz (I know it's been years, this comment is for new readers) I agree in the general case. But my app loads a third party library that takes some file path as initialization data but segfaults if the file does not exist. I think this is a valid scenario for checking if the file exists withou trying to open it to be able to report the error without a fatal crash, as my code doesn't need to read file contents or write to the file directly. – Sergio Acosta Feb 21 '19 at 17:31

14 Answers14

1023

To check if a file doesn't exist, equivalent to Python's if not os.path.exists(filename):

if _, err := os.Stat("/path/to/whatever"); errors.Is(err, os.ErrNotExist) {
  // path/to/whatever does not exist
}

To check if a file exists, equivalent to Python's if os.path.exists(filename):

Edited: per recent comments

if _, err := os.Stat("/path/to/whatever"); err == nil {
  // path/to/whatever exists

} else if errors.Is(err, os.ErrNotExist) {
  // 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


}
Rambatino
  • 4,716
  • 1
  • 33
  • 56
Sridhar Ratnakumar
  • 81,433
  • 63
  • 146
  • 187
  • 6
    sometimes it return ENOTDIR instead of `NOTEXIST`, for example, if `/etc/bashrc` exist, the `/etc/bashrc/foobar` will return `ENOTDIR` – lidaobing Nov 23 '13 at 15:23
  • 57
    The second snippet is more subtly wrong; the condition should be `!os.IsNotExist(err)`. It's possible the file exists but `os.Stat` fails for other reasons (eg. permission, failing disk). Using `err == nil` as the condition incorrectly categorises such failures as "the file does not exist". – sqweek Jul 23 '15 at 04:19
  • 10
    To check if a file exists is wrong: err is nil if file exists – tangxinfa Jun 29 '17 at 03:01
  • 2
    Make sure to expand ~ or else it will return false... https://stackoverflow.com/questions/17609732/expand-tilde-to-home-directory/43578461#43578461 – Marcello DeSales Feb 21 '18 at 00:41
  • You could use os.IsExist() depending the case, could be more idiomatically instead of making a double negation when doing !os.IsNotExistant() – Ariel Monaco Feb 17 '20 at 03:18
  • 8
    The latest docs tell you to use `errors.Is(err, os.ErrNotExist)` in any new code: https://github.com/golang/go/blob/master/src/os/error.go#L90-L91 – Pal Kerecsenyi Dec 28 '20 at 18:25
  • @PalKerecsenyi Thanks for mentioning that! I think it's noteworthy enough that it's worth being its own answer to this question, so I [posted a new answer](https://stackoverflow.com/a/66405130/2338327) that mentions `errors.Is(err, os.ErrNotExist`). – Dave Yarwood Feb 28 '21 at 01:44
  • 1
    @PalKerecsenyi the comment linked suggests `fs.ErrNotExist` because `os.ErrNotExist` is specific to the`os` package. The correct check is `errors.Is(err, fs.ErrNotExist)`. – KernelDeimos Jul 19 '21 at 20:46
  • If you want to check if a lot of files exist, you can replace `_,. err := os.Stat(path)` with: `var fs syscall.Stat_t; err := syscall.Stat(path, &fs)`, and you'll save yourself two allocs. – Jason Walton Nov 04 '21 at 20:15
157

Answer by Caleb Spare posted in gonuts mailing list.

[...] It's not actually needed very often and [...] using os.Stat is easy enough for the cases where it is required.

[...] For instance: if you are going to open the file, there's no reason to check whether it exists first. The file could disappear in between checking and opening, and anyway you'll need to check the os.Open error regardless. So you simply call os.IsNotExist(err) after you try to open the file, and deal with its non-existence there (if that requires special handling).

[...] You don't need to check for the paths existing at all (and you shouldn't).

  • os.MkdirAll works whether or not the paths already exist. (Also you need to check the error from that call.)

  • Instead of using os.Create, you should use os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666) . That way you'll get an error if the file already exists. Also this doesn't have a race condition with something else making the file, unlike your version which checks for existence beforehand.

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

NatNgs
  • 874
  • 14
  • 25
OscarRyz
  • 196,001
  • 113
  • 385
  • 569
  • 1
    Thank you for quoting this because I had a lot of trouble finding how to create a file only if the file does not exist (`os.O_EXCL`) because I can never remember what these flags mean. I thought `os.O_CREATE` already implied that the file must be created and not exist beforehand. – natiiix Feb 18 '22 at 17:48
68

The first thing to consider is that it is rare that you would only want to check whether or not a file exists. In most situations, you're trying to do something with the file if it exists. In Go, any time you try to perform some operation on a file that doesn't exist, the result should be a specific error (os.ErrNotExist) and the best thing to do is check whether the return err value (e.g. when calling a function like os.OpenFile(...)) is os.ErrNotExist.

The recommended way to do this used to be:

file, err := os.OpenFile(...)
if os.IsNotExist(err) {
    // handle the case where the file doesn't exist
}

However, since the addition of errors.Is in Go 1.13 (released in late 2019), the new recommendation is to use errors.Is:

file, err := os.OpenFile(...)
if errors.Is(err, os.ErrNotExist) {
    // handle the case where the file doesn't exist
}

It's usually best to avoid using os.Stat to check for the existence of a file before you attempt to do something with it, because it will always be possible for the file to be renamed, deleted, etc. in the window of time before you do something with it.

However, if you're OK with this caveat and you really, truly just want to check whether a file exists without then proceeding to do something useful with it (as a contrived example, let's say that you're writing a pointless CLI tool that tells you whether or not a file exists and then exits ¯\_(ツ)_/¯), then the recommended way to do it would be:

if _, err := os.Stat(filename); errors.Is(err, os.ErrNotExist) {
    // file does not exist
} else {
    // file exists
}
Dave Yarwood
  • 2,866
  • 1
  • 17
  • 29
  • I have a question how do u know if is a file or a directory using this method? – Teocci Sep 01 '21 at 00:34
  • Assuming you're talking about the (not recommended!) `os.Stat` approach: `os.Stat` returns `info, err` and if `err` is nil, `info` is a FileInfo object that you can use to determine whether the thing that exists is a file or a directory. See [this other answer](https://stackoverflow.com/a/57791506/2338327) for more details. – Dave Yarwood Sep 16 '21 at 19:13
  • No I was asking about the recommended method `file, err := os.OpenFile(...) if errors.Is(err, os.ErrNotExist) {` this is nice when you already have the file but sometimes you get a dir or a file if a file you do something if no, and is a dir then you do some other. There are 100000 of examples using the `os.Stat` and you mentioned that we should avoid the uses of that that is why I asked. How to use the recommended method and check if is dir or handle this scenario. – Teocci Sep 17 '21 at 02:08
  • Good question. I'm not sure, off hand, what would happen if you called OpenFile on a directory. If it returns an error, then I think in most cases it would be sufficient to handle the error the same way you would handle any other error. – Dave Yarwood Sep 18 '21 at 04:36
  • 2
    shark attacks are rare, except at the beach. the need to specifically check if a file exists is rare, except under the question titled “How to check if a file exists in Go”. in my case, if the file exists, my work is done; if it doesn't exist, i must generate it. – Mattias Martens Mar 29 '22 at 22:39
  • That's a good counterexample! – Dave Yarwood Mar 30 '22 at 15:52
  • ...although you could use `os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)` to create the file only if it doesn't already exist, as mentioned in another answer. – Dave Yarwood Jul 17 '22 at 16:34
55

You should use the os.Stat() and os.IsNotExist() functions as in the following example:

func Exists(name string) (bool, error) {
    _, err := os.Stat(name)
    if err == nil {
        return true, nil
    }
    if errors.Is(err, os.ErrNotExist) {
        return false, nil
    }
    return false, err
}

edit1: fixed issue of returning true when under some circumstances.
edit2: switched to using errors.Is() from os.IsNotExist(), which many say is a best-practice and here

Cameron
  • 2,903
  • 1
  • 30
  • 31
  • 19
    Beware: as http://stackoverflow.com/a/22467409/712014 pointed out, this code returns true, even if the file does not exist, for example when Stat() returns permission denied. – Michael Sep 07 '15 at 18:47
  • go build undefined: errors – CS QGB Jul 09 '22 at 13:08
30

What other answers missed, is that the path given to the function could actually be a directory. Following function makes sure, that the path is really a file.

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

Another thing to point out: This code could still lead to a race condition, where another thread or process deletes or creates the specified file, while the fileExists function is running.

If you're worried about this, use a lock in your threads, serialize the access to this function or use an inter-process semaphore if multiple applications are involved. If other applications are involved, outside of your control, you're out of luck, I guess.

ZuBsPaCe
  • 540
  • 6
  • 8
  • If other applications are involved, you might want to keep your files in a separate folder. If you are creating new files, O_EXCL is your friend to avoid races (if the platform you are using supports that flag). – Olli Jan 27 '21 at 13:53
  • Do not use this cause `info` could be nil when calling `return !info.IsDir()` – igonejack Mar 05 '21 at 15:15
23

The example by user11617 is incorrect; it will report that the file exists even in cases where it does not, but there was an error of some other sort.

The signature should be Exists(string) (bool, error). And then, as it happens, the call sites are no better.

The code he wrote would better as:

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

But I suggest this instead:

func Exists(name string) (bool, error) {
  _, err := os.Stat(name)
  if os.IsNotExist(err) {
    return false, nil
  }
  return err != nil, err
}
Afriza N. Arief
  • 7,696
  • 5
  • 47
  • 74
user3431012
  • 277
  • 2
  • 2
13
    _, 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
  • 1,410
  • 13
  • 12
10

basicly


package main

import (
    "fmt"
    "os"
)

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

func main() {

    var file string = "foo.txt"
    exist := fileExists(file)
    
    if exist {
        fmt.Println("file exist")
    } else {

        fmt.Println("file not exists")
    }

}

run example

other way

with os.Open

package main

import (
    "fmt"
    "os"
)

func fileExists(path string) bool {
    _, err := os.Open(path) // For read access.
    return err == nil

}

func main() {

    fmt.Println(fileExists("d4d.txt"))

}


run it

dılo sürücü
  • 3,821
  • 1
  • 26
  • 28
9

Best way to check if file exists:

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

The function example:

func file_is_exists(f string) bool {
    _, err := os.Stat(f)
    if os.IsNotExist(err) {
        return false
    }
    return err == nil
}
honmaple
  • 889
  • 1
  • 8
  • 12
  • 2
    Isn't the if redundant? – Ilia Choly Jan 12 '18 at 22:00
  • it's not, but the logic is broken currently. The function should only return false if the file does not exist, however currently it returns false on any error. Possibly it should panic instead. Also, "file is exists" is broken English. – LogicDaemon Jul 26 '21 at 07:16
8

Let's look at few aspects first, both the function provided by os package of golang are not utilities but error checkers, what do I mean by that is they are just a wrapper to handle errors on cross platform.

So basically if os.Stat if this function doesn't give any error that means the file is existing if it does you need to check what kind of error it is, here comes the use of these two function os.IsNotExist and os.IsExist.

This can be understood as the Stat of the file throwing error because it doesn't exists or is it throwing error because it exist and there is some problem with it.

The parameter that these functions take is of type error, although you might be able to pass nil to it but it wouldn't make sense.

This also points to the fact that IsExist is not same as !IsNotExist, they are way two different things.

So now if you want to know if a given file exist in go, I would prefer the best way is:

if _, err := os.Stat(path/to/file); !os.IsNotExist(err){
   //TODO
} 
Farhaan Bukhsh
  • 178
  • 2
  • 7
7

As mentioned in other answers, it is possible to construct the required behaviour / errors from using different flags with os.OpenFile. In fact, os.Create is just a sensible-defaults shorthand for doing so:

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

You should combine these flags yourself to get the behaviour you are interested in:

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

Depending on what you pick, you will get different errors.

Below is an example which will either truncate an existing file, or fail when a file exists.

openOpts := os.O_RDWR|os.O_CREATE
if truncateWhenExists {
    openOpts |= os.O_TRUNC // file will be truncated
} else {
    openOpts |= os.O_EXCL  // file must not exist
}
f, err := os.OpenFile(filePath, openOpts, 0644)
// ... do stuff
Sebastian N
  • 759
  • 7
  • 13
6

Here is my take on a file exists method. It also checks that the file is not a directory and in case of an error, returns it as well.

// FileExists checks if a file exists (and it is not a directory).
func FileExists(filePath string) (bool, error) {
    info, err := os.Stat(filePath)
    if err == nil {
        return !info.IsDir(), nil
    }
    if errors.Is(err, os.ErrNotExist) {
        return false, nil
    }
    return false, err
}
Roemer
  • 2,012
  • 1
  • 24
  • 26
5

This is how I check if a file exists in Go 1.16

package main

import (
    "errors"
    "fmt"
    "io/fs"
    "os"
)

func main () {
    if _, err:= os.Stat("/path/to/file"); errors.Is(err, fs.ErrNotExist){
        fmt.Print(err.Error())
    } else {
        fmt.Print("file exists")
    }
}
Suraj Sharma
  • 777
  • 6
  • 10