File Systems
Examples in
Go
package main
import (
"fmt"
"os"
)
func main() {
file, e := os.Stat("/some/path")
if e != nil {
panic(e)
}
mode := file.Mode()
if mode.IsDir() {
fmt.Println("directory")
} else if mode.IsRegular() {
fmt.Println("file")
}
}
package main
import (
"fmt"
"os"
)
func exists(path string) bool {
if _, err := os.Stat(path); os.IsNotExist(err) {
return false
}
return true
}
func main() {
fmt.Println(exists("./my/file.txt"))
}
package main
import (
"bufio"
"fmt"
"io"
"io/ioutil"
"os"
)
func check(e error) {
if e != nil {
panic(e)
}
}
func main() {
// Perhaps the most basic file reading task is slurping a file’s entire contents into memory.
dat, err := ioutil.ReadFile("/tmp/dat")
check(err)
fmt.Print(string(dat))
// You’ll often want more control over how and what parts of a file are read.
// For these tasks, start by Opening a file to obtain an os.File value.
f, err := os.Open("/tmp/dat")
check(err)
// Read some bytes from the beginning of the file.
// Allow up to 5 to be read but also note how many actually were read.
b1 := make([]byte, 5)
n1, err := f.Read(b1)
check(err)
fmt.Printf("%d bytes: %s\n", n1, string(b1[:n1]))
// You can also Seek to a known location in the file and Read from there.
o2, err := f.Seek(6, 0)
check(err)
b2 := make([]byte, 2)
n2, err := f.Read(b2)
check(err)
fmt.Printf("%d bytes @ %d: ", n2, o2)
fmt.Printf("%v\n", string(b2[:n2]))
// The io package provides some functions that may be helpful for file reading.
// For example, reads like the ones above can be more robustly implemented with ReadAtLeast.
o3, err := f.Seek(6, 0)
check(err)
b3 := make([]byte, 2)
n3, err := io.ReadAtLeast(f, b3, 2)
check(err)
fmt.Printf("%d bytes @ %d: %s\n", n3, o3, string(b3))
// There is no built-in rewind, but Seek(0, 0) accomplishes this.
_, err = f.Seek(0, 0)
check(err)
// The bufio package implements a buffered reader that may be useful both for its efficiency
// with many small reads and because of the additional reading methods it provides.
r4 := bufio.NewReader(f)
b4, err := r4.Peek(5)
check(err)
fmt.Printf("5 bytes: %s\n", string(b4))
f.Close()
}
package main
import (
"bufio"
"fmt"
"io/ioutil"
"os"
)
func check(e error) {
if e != nil {
panic(e)
}
}
func main() {
// To start, here’s how to dump a string (or just bytes) into a file.
d1 := []byte("hello\ngo\n")
err := ioutil.WriteFile("/tmp/dat1", d1, 0644)
check(err)
// For more granular writes, open a file for writing.
f, err := os.Create("/tmp/dat2")
check(err)
// It’s idiomatic to defer a Close immediately after opening a file.
defer f.Close()
// You can Write byte slices as you’d expect.
d2 := []byte{115, 111, 109, 101, 10}
n2, err := f.Write(d2)
check(err)
fmt.Printf("wrote %d bytes\n", n2)
// A WriteString is also available.
n3, err := f.WriteString("writes\n")
fmt.Printf("wrote %d bytes\n", n3)
// Issue a Sync to flush writes to stable storage.
f.Sync()
// bufio provides buffered writers in addition to the buffered readers we saw earlier.
w := bufio.NewWriter(f)
n4, err := w.WriteString("buffered\n")
fmt.Printf("wrote %d bytes\n", n4)
// Use Flush to ensure all buffered operations have been applied to the underlying writer.
w.Flush()
}
The filepath package provides functions to parse and construct file paths in a way that is portable between operating systems; dir/file on Linux vs. dir\file on Windows, for example.
package main
import (
"fmt"
"path/filepath"
"strings"
)
func main() {
// Join should be used to construct paths in a portable way.
// It takes any number of arguments and constructs a hierarchical path from them.
p := filepath.Join("dir1", "dir2", "filename")
fmt.Println("p:", p)
// You should always use Join instead of concatenating /s or \s manually.
// In addition to providing portability, Join will also normalize paths by
// removing superfluous separators and directory changes.
fmt.Println(filepath.Join("dir1//", "filename"))
fmt.Println(filepath.Join("dir1/../dir1", "filename"))
// Dir and Base can be used to split a path to the directory and the file.
// Alternatively, Split will return both in the same call.
fmt.Println("Dir(p):", filepath.Dir(p))
fmt.Println("Base(p):", filepath.Base(p))
// We can check whether a path is absolute.
fmt.Println(filepath.IsAbs("dir/file"))
fmt.Println(filepath.IsAbs("/dir/file"))
filename := "config.json"
// Some file names have extensions following a dot.
// We can split the extension out of such names with Ext.
ext := filepath.Ext(filename)
fmt.Println(ext)
// To find the file’s name with the extension removed, use strings.TrimSuffix.
fmt.Println(strings.TrimSuffix(filename, ext))
// Rel finds a relative path between a base and a target.
// It returns an error if the target cannot be made relative to base.
rel, err := filepath.Rel("a/b", "a/b/t/file")
if err != nil {
panic(err)
}
fmt.Println(rel)
rel, err = filepath.Rel("a/b", "a/c/t/file")
if err != nil {
panic(err)
}
fmt.Println(rel)
}
Last Run
:
p: dir1/dir2/filename
dir1/filename
dir1/filename
Dir(p): dir1/dir2
Base(p): filename
false
true
.json
config
t/file
../c/t/file
Go has several useful functions for working with directories in the file system.
package main
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
)
func check(e error) {
if e != nil {
panic(e)
}
}
func main() {
// Create a new sub-directory in the current working directory.
err := os.Mkdir("subdir", 0755)
check(err)
// When creating temporary directories, it’s good practice to defer their removal.
// os.RemoveAll will delete a whole directory tree (similarly to rm -rf).
defer os.RemoveAll("subdir")
createEmptyFile := func(name string) {
d := []byte("")
check(ioutil.WriteFile(name, d, 0644))
}
createEmptyFile("subdir/file1")
// We can create a hierarchy of directories, including parents with MkdirAll.
// This is similar to the command-line mkdir -p.
err = os.MkdirAll("subdir/parent/child", 0755)
check(err)
createEmptyFile("subdir/parent/file2")
createEmptyFile("subdir/parent/file3")
createEmptyFile("subdir/parent/child/file4")
// ReadDir lists directory contents, returning a slice of os.FileInfo objects.
c, err := ioutil.ReadDir("subdir/parent")
check(err)
fmt.Println("Listing subdir/parent")
for _, entry := range c {
fmt.Println(" ", entry.Name(), entry.IsDir())
}
// Chdir lets us change the current working directory, similarly to cd.
err = os.Chdir("subdir/parent/child")
check(err)
// Now we’ll see the contents of subdir/parent/child when listing the current directory.
c, err = ioutil.ReadDir(".")
check(err)
fmt.Println("Listing subdir/parent/child")
for _, entry := range c {
fmt.Println(" ", entry.Name(), entry.IsDir())
}
err = os.Chdir("../../..")
check(err)
// We can also visit a directory recursively, including all its sub-directories.
// Walk accepts a callback function to handle every file or directory visited.
fmt.Println("Visiting subdir")
err = filepath.Walk("subdir", visit)
}
func visit(p string, info os.FileInfo, err error) error {
if err != nil {
return err
}
fmt.Println(" ", p, info.IsDir())
return nil
}
Last Run
:
Listing subdir/parent
child true
file2 false
file3 false
Listing subdir/parent/child
file4 false
Visiting subdir
subdir true
subdir/file1 false
subdir/parent true
subdir/parent/child true
subdir/parent/child/file4 false
subdir/parent/file2 false
subdir/parent/file3 false
Throughout program execution, we often want to create data that isn’t needed after the program exits. Temporary files and directories are useful for this purpose since they don’t pollute the file system over time.
package main
import (
"fmt"
"io/ioutil"
"os"
)
func check(e error) {
if e != nil {
panic(e)
}
}
func main() {
// The easiest way to create a temporary file is by calling ioutil.TempFile.
// It creates a file and opens it for reading and writing. We provide "" as the first argument,
// so ioutil.TempFile will create the file in the default location for our OS.
f, err := ioutil.TempFile("", "sample")
check(err)
// Display the name of the temporary file.
// On Unix-based OSes the directory will likely be /tmp.
// The file name starts with the prefix given as the second argument to ioutil.TempFile
// and the rest is chosen automatically to ensure that concurrent calls will always
// create different file names.
fmt.Println("Temp file name:", f.Name())
// Clean up the file after we’re done. The OS is likely to clean up temporary files by itself after some time,
// but it’s good practice to do this explicitly.
defer os.Remove(f.Name())
_, err = f.Write([]byte{1, 2, 3, 4})
check(err)
}
Last Run
:
Temp file name: /tmp/sample135820989
Throughout program execution, we often want to create data that isn’t needed after the program exits. Temporary files and directories are useful for this purpose since they don’t pollute the file system over time.
package main
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
)
func check(e error) {
if e != nil {
panic(e)
}
}
func main() {
// If we intend to write many temporary files, we may prefer to create a temporary directory.
// ioutil.TempDir’s arguments are the same as TempFile’s, but it returns a directory name rather than an open file.
dname, err := ioutil.TempDir("", "sampledir")
fmt.Println("Temp dir name:", dname)
defer os.RemoveAll(dname)
fname := filepath.Join(dname, "file1")
err = ioutil.WriteFile(fname, []byte{1, 2}, 0666)
check(err)
}
Last Run
:
Temp dir name: /tmp/sampledir562555487
package main
import (
"fmt"
"path/filepath"
)
func main() {
fmt.Println(filepath.Ext("hello.go"))
}
Last Run
:
.go
package main
import (
"fmt"
"io/ioutil"
)
func main() {
files, err := ioutil.ReadDir("./some/dir")
if err == nil {
panic("oops")
}
for _, file := range files {
fmt.Println(file.Name())
}
}
package main
import (
"os"
)
func main() {
err := os.Remove("./some/file.txt")
if err != nil {
panic(err)
}
}