Essential Go File I/O  Edit on GitHub      File Issue

File operations

Get file size

// GetFileSize returns file size or error if e.g. file doesn't exist
func GetFileSize(path string) (int64, error) {
	st, err := os.Lstat(path)
	if err != nil {
		return -1, err
	}
	return st.Size(), nil
}

func main() {
	path := "file_size.go"
	size, err := GetFileSize(path)
	if err != nil {
		log.Fatalf("GetFileSize failed with '%s'\n", err)
	}
	fmt.Printf("File %s is %d bytes in size\n", path, size)
}
File file_size.go is 483 bytes in size

Instead of os.Stat we can also use os.Lstat. The difference is that os.Stat follows symbolic links and os.Lstat doesn’t.

In other words: for a symbolic link os.Lstat returns information about link and os.Stat about the file that it links to.

Get information about the file

func main() {
	st, err := os.Stat("file_info.go")
	if err != nil {
		log.Fatalf("GetFileSize failed with '%s'\n", err)
	}
	fmt.Printf(`Name: %s
Size: %d
IsDir: %v
Mode: %x
ModTime: %s
OS info: %T
`, st.Name(), st.Size(), st.IsDir(), st.Mode, st.ModTime(), st.Sys())
}
Name: file_info.go
Size: 344
IsDir: false
Mode: 1091c40
ModTime: 2018-02-25 23:52:31.004823169 -0800 PST
OS info: *syscall.Stat_t

Check if a file exists

// IsPathxists returns true if a given path exists, false if it doesn't.
// It might return an error if e.g. file exists but you don't have
// access
func IsPathxists(path string) (bool, error) {
	_, err := os.Lstat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	// error other than not existing e.g. permission denied
	return false, err
}

func printExists(path string) {
	exists, err := IsPathxists(path)
	if err == nil {
		fmt.Printf("File '%s' exists: %v\n", path, exists)
	} else {
		fmt.Printf("IsFileExists('%s') failed with '%s'\n", path, err)
	}
}
func main() {
	printExists("file_exists.go")
	printExists("non-existent-file.txt")
}
File 'file_exists.go' exists: true
File 'non-existent-file.txt' exists: false

Checking if a file exists is surprisingly tricky and it’s impossible to write a generic function that handles all the nuances.

Here are decisions we made:

Delete a file

path := "foo.txt"
err := os.Remove(path)
if err != nil {
    if os.IsNotExist(err) {
        fmt.Printf("os.Remove failed because file doesn't exist\n")
    } else {
        fmt.Printf("os.Remove failed with '%s'\n", err)
    }
}

os.Remove returns an error for files that don’t exist.

Usually you want to ignore such errors which you can do by testing error with os.IsNotExist(err).

Rename a file

oldPath := "old_name.txt"
newPath := "new_name.txt"
err := os.Rename(oldPath, newPath)
if err != nil {
    fmt.Printf("os.Rename failed with '%s'\n", err)
}

Copy a file

// CopyFile copies a src file to dst
func CopyFile(dst, src string) error {
	srcFile, err := os.Open(src)
	if err != nil {
		return err
	}
	defer srcFile.Close()

	dstFile, err := os.Create(dst)
	if err != nil {
		return err
	}
	_, err = io.Copy(dstFile, srcFile)
	err2 := dstFile.Close()
	if err == nil && err2 != nil {
		err = err2
	}
	if err != nil {
		// delete the destination if copy failed
		os.Remove(dst)
	}
	return err
}

Writing a generic function for copying files is tricky and it’s impossible to write a function that serves all use cases.

Here are policy decisions we made:

If you want different behavior, you will have to modify the code as needed.

  ↑ ↓ to navigate     ↵ to select     Esc to close