How to check if a file exists in Go? - file

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?

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
}

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

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
}

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

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.

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
}

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

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

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
}

The function example:
func file_is_exists(f string) bool {
_, err := os.Stat(f)
if os.IsNotExist(err) {
return false
}
return err == nil
}

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
}

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

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

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
}

Related

How to check file `name` (with any extension) does exist in the directory in Golang?

I know I could check the file does exist or not in Golang by the answers of the following questions.
How to check whether a file or directory denoted by a path exists in Golang?
How to check if a file exists in Go?
Gist - Check if file or directory exists in Golang
The code looks like this.
_, err := os.Stat(path)
if err == nil {
log.Printf("File %s exists.", path)
} else if os.IsNotExist(err) {
log.Printf("File %s not exists.", path)
} else {
log.Printf("File %s stat error: %v", path, err)
}
But here's my real question, how do I check the filename does exist (has been used) in the specified directory? For example if I have a file tree like this:
--- uploads
|- foo.png
|- bar.mp4
I wanted to check if there's any file is using the specified name..
used := filenameUsed("uploads/foo")
fmt.Println(used) // Output: true
used = filenameUsed("uploads/hello")
fmt.Println(used) // Output: false
How do I implement the filenameUsed function?
Google gave me a path/filepath package as the result but I have no clue about how to use it.
You may use the filepath.Glob() function where you can specify a pattern to list files.
The pattern to be used is basically the name you wish to check if used, extended with the any extension pattern.
Example:
func filenameUsed(name string) (bool, error) {
matches, err := filepath.Glob(name + ".*")
if err != nil {
return false, err
}
return len(matches) > 0, nil
}
Using / testing it:
fmt.Print("Filename foo used: ")
fmt.Println(filenameUsed("uploads/foo"))
fmt.Print("Filename bar used: ")
fmt.Println(filenameUsed("uploads/bar"))
Example output:
Filename foo used: true <nil>
Filename bar used: false <nil>
However, note that filenameUsed() returning false (and nil error) does not mean a file with that name won't exist if you attempt to create one after. Meaning checking it and attempting to create such a file does not guarantee atomicity. If your purpose is to create a file if the name is not used, then simply try to create the file in the proper mode (do not overwrite if exists), and handle the (creation) error returned by that call.

Go. Writing []byte to file results in zero byte file

I try to serialize a structured data to file. I looked through some examples and made such construction:
func (order Order) Serialize(folder string) {
b := bytes.Buffer{}
e := gob.NewEncoder(&b)
err := e.Encode(order)
if err != nil { panic(err) }
os.MkdirAll(folder, 0777)
file, err := os.Create(folder + order.Id)
if err != nil { panic(err) }
defer file.Close()
writer := bufio.NewWriter(file)
n, err := writer.Write(b.Bytes())
fmt.Println(n)
if err != nil {
panic(err)
}
}
Serialize is a method serializing its object to file called by it's id property. I looked through debugger - byte buffer contains data before writing. I mean object is fully initialized. Even n variable representing quantity of written bytes is more than a thousand - the file shouldn't be empty at all. The file is created but it is totally empty. What's wrong?
bufio.Writer (as the package name hints) uses a buffer to cache writes. If you ever use it, you must call Writer.Flush() when you're done writing to it to ensure the buffered data gets written to the underlying io.Writer.
Also note that you can directly write to an os.File, no need to create a buffered writer "around" it. (*os.File implements io.Writer).
Also note that you can create the gob.Encoder directly directed to the os.File, so even the bytes.Buffer is unnecessary.
Also os.MkdirAll() may fail, check its return value.
Also it's better to "concatenate" parts of a file path using filepath.Join() which takes care of extra / missing slashes at the end of folder names.
And last, it would be better to signal the failure of Serialize(), e.g. with an error return value, so the caller party has the chance to examine if the operation succeeded, and act accordingly.
So Order.Serialize() should look like this:
func (order Order) Serialize(folder string) error {
if err := os.MkdirAll(folder, 0777); err != nil {
return err
}
file, err := os.Create(filepath.Join(folder, order.Id))
if err != nil {
return err
}
defer file.Close()
if err := gob.NewEncoder(file).Encode(order); err != nil {
return err
}
return nil
}

Golang, a proper way to rewind file pointer

package main
import (
"bufio"
"encoding/csv"
"fmt"
"io"
"log"
"os"
)
func main() {
data, err := os.Open("cc.csv")
defer data.Close()
if err != nil {
log.Fatal(err)
}
s := bufio.NewScanner(data)
for s.Scan() {
fmt.Println(s.Text())
if err := s.Err(); err != nil {
panic(err)
}
}
// Is it a proper way?
data.Seek(0, 0)
r := csv.NewReader(data)
for {
if record, err := r.Read(); err == io.EOF {
break
} else if err != nil {
log.Fatal(err)
} else {
fmt.Println(record)
}
}
}
I use two readers here to read from a csv file.
To rewind a file I use data.Seek(0, 0) is it a good way? Or it's better to close the file and open again before second reading.
Is it also correct to use *File as an io.Reader ? Or it's better to do r := ioutil.NewReader(data)
Seeking to the beginning of the file is easiest done using File.Seek(0, 0) (or more safely using a constant: File.Seek(0, io.SeekStart)) just as you suggested, but don't forget that:
The behavior of Seek on a file opened with O_APPEND is not specified.
(This does not apply to your example though.)
Setting the pointer to the beginning of the file is always much faster than closing and reopening the file. If you need to read different, "small" parts of the file many times, alternating, then maybe it might be profitable to open the file twice to avoid repeated seeking (worry about this only if you have peformance problems).
And again, *os.File implements io.Reader, so you can use it as an io.Reader. I don't know what ioutil.NewReader(data) is you mentioned in your question (package io/ioutil has no such function; maybe you meant bufio.NewReader()?), but certainly it is not needed to read from a file.

How to write a safe rename in Go? (Or, how to write this Python in Go?)

I've got the following code in Python:
if not os.path.exists(src): sys.exit("Does not exist: %s" % src)
if os.path.exists(dst): sys.exit("Already exists: %s" % dst)
os.rename(src, dst)
From this question, I understand that there is no direct method to test if a file exists or doesn't exist.
What is the proper way to write the above in Go, including printing out the correct error strings?
Here is the closest I've gotten:
package main
import "fmt"
import "os"
func main() {
src := "a"
dst := "b"
e := os.Rename(src, dst)
if e != nil {
fmt.Println(e.(*os.LinkError).Op)
fmt.Println(e.(*os.LinkError).Old)
fmt.Println(e.(*os.LinkError).New)
fmt.Println(e.(*os.LinkError).Err)
}
}
From the availability of information from the error, where it effectively doesn't tell you what the problem is without you parsing an English freeformat string, it seems to me that it is not possible to write the equivalent in Go.
The code you provide contains a race condition: Between you checking for dst to not exist and copying something into dst, a third party could have created the file dst, causing you to overwrite a file. Either remove the os.path.exists(dst) check because it cannot reliably detect if the target exists at the time you try to remove it, or employ the following algorithm instead:
Create a hardlink from src to dst. If a file named dst exists, the operation will fail and you can bail out. If src does not exist, the operation will fail, too.
Remove src.
The following code implements the two-step algorithm outlined above in Go.
import "os"
func renameAndCheck(src, dst string) error {
err := os.Link(src, dst)
if err != nil {
return err
}
return os.Remove(src)
}
You can check for which reason the call to os.Link() failed:
If the error satisfies os.IsNotExist(), the call failed because src did not exist at the time os.Link() was called
If the error satisfies os.IsExist(), the call failed because dst exists at the time os.Link() is called
If the error satisfies os.IsPermission(), the call failed because you don't have sufficient permissions to create a hard link
As far as I know, other reasons (like the file system not supporting the creation of hard links or src and dst being on different file systems) cannot be tested portably.
The translation if your Python code to Go is:
if _, err := os.Stat(src); err != nil {
// The source does not exist or some other error accessing the source
log.Fatal("source:", err)
}
if _, err := os.Stat(dst); !os.IsNotExists(dst) {
// The destination exists or some other error accessing the destination
log.Fatal("dest:", err)
}
if err := os.Rename(src, dst); err != nil {
log.Fatal(err)
}
The three function call sequence is not safe (I am referring to both the original Python version and my replication of it here). The source can be removed or the destination can be created after the checks, but before the rename.
The safe way to move a file is OS dependent. On Windows, you can just call os.Rename(). On Windows, this function will fail if the destination exists or the source does not. On Posix systems, you should link and remove as described in another answer.

How to check whether a file or directory exists? [duplicate]

This question already has answers here:
How to check if a file exists in Go?
(14 answers)
Closed 2 years ago.
I want to check the existence of file ./conf/app.ini in my Go code,
but I can't find a good way to do that.
I know there is a method of File in Java: public boolean exists(), which returns true if the file or directory exists.
But how can this be done in Go?
// exists returns whether the given file or directory exists
func exists(path string) (bool, error) {
_, err := os.Stat(path)
if err == nil { return true, nil }
if os.IsNotExist(err) { return false, nil }
return false, err
}
Edited to add error handling.
You can use this :
if _, err := os.Stat("./conf/app.ini"); err != nil {
if os.IsNotExist(err) {
// file does not exist
} else {
// other error
}
}
See : http://golang.org/pkg/os/#IsNotExist
More of an FYI, since I looked around for a few minutes thinking my question be a quick search away.
How to check if path represents an existing directory in Go?
This was the most popular answer in my search results, but here and elsewhere the solutions only provide existence check. To check if path represents an existing directory, I found I could easily:
path := GetSomePath();
if stat, err := os.Stat(path); err == nil && stat.IsDir() {
// path is a directory
}
Part of my problem was that I expected path/filepath package to contain the isDir() function.
Simple way to check whether file exists or not:
if _, err := os.Stat("/path/to/whatever"); os.IsNotExist(err) {
// path/to/whatever does not exist
}
if _, err := os.Stat("/path/to/whatever"); err == nil {
// path/to/whatever exists
}
Sources:
mattes made this gist on Aug 6 '14
Sridhar Ratnakumar made this answer on Sep 21 '12
I use the following function to check my directories for any errors. It's very similar to previous answers, but I think not nesting ifs makes the code more clear. It uses go-homedir to remove ~ from directory paths and pkg/errors to return nicer error messages, but it would be easy to take them out if you don't need their functionality.
// validateDirectory expands a directory and checks that it exists
// it returns the full path to the directory on success
// validateDirectory("~/foo") -> ("/home/bbkane/foo", nil)
func validateDirectory(dir string) (string, error) {
dirPath, err := homedir.Expand(dir)
if err != nil {
return "", errors.WithStack(err)
}
info, err := os.Stat(dirPath)
if os.IsNotExist(err) {
return "", errors.Wrapf(err, "Directory does not exist: %v\n", dirPath)
}
if err != nil {
return "", errors.Wrapf(err, "Directory error: %v\n", dirPath)
}
if !info.IsDir() {
return "", errors.Errorf("Directory is a file, not a directory: %#v\n", dirPath)
}
return dirPath, nil
}
Also, to repeat #Dave C's comment, if the reason you're checking a directory's existence is to write a file into it, it's usually better to simply try to open it an deal with errors afterwards:
// O_EXCL - used with O_CREATE, file must not exist
file, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
if err != nil {
return errors.WithStack(err)
}
defer file.Close()
There is simple way to check whether your file exists or not:
if _, err := os.Stat("./conf/app.ini"); err != nil {
if os.IsNotExist(err) {
..... //Shows error if file not exists
} else {
..... // Shows success message like file is there
}
}

Resources