How to check if a file exists in Go?

前端 未结 11 1747
陌清茗
陌清茗 2020-11-30 16:23

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

相关标签:
11条回答
  • 2020-11-30 16:32

    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
    }
    
    0 讨论(0)
  • 2020-11-30 16:38

    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"); os.IsNotExist(err) {
      // 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 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
    
    
    }
    
    0 讨论(0)
  • 2020-11-30 16:39

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

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

    The example is extracted from here.

    0 讨论(0)
  • 2020-11-30 16:43

    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

    0 讨论(0)
  • 2020-11-30 16:45

    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.

    0 讨论(0)
  • 2020-11-30 16:47

    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.

    Here's an example where I wish to open a file for writing, but I will only truncate an existing file if the user has said that is 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 讨论(0)
提交回复
热议问题