os

Guided tour · I/O & Files · pkg.go.dev →

Portable OS interface: files, env, args, signals, processes.

Talk to the operating system: open files, read env, args, exit, signals, processes.

Read a whole file
b, err := os.ReadFile("path")
Write a whole file
err := os.WriteFile("path", data, 0644)
Open for streaming
f, err := os.Open("path")
if err != nil { return err }
defer f.Close()
Create a new file
f, err := os.Create("path")
Does it exist?
_, err := os.Stat(path)
missing := errors.Is(err, fs.ErrNotExist)
Env var with default
v := cmp.Or(os.Getenv("PORT"), "8080")
CLI args
args := os.Args[1:]
Exit (skips deferred funcs!)
os.Exit(1)

Args, environment, exit

os.Args — command-line arguments

os.Args[0] is the program name. For richer parsing use the flag package.

fmt.Println(os.Args)

Getenv, LookupEnv, Setenv

LookupEnv distinguishes 'unset' from 'empty'. Prefer it when empty has meaning.

os.Setenv("NAME", "ada")
v := os.Getenv("NAME")              // "ada"
v, ok := os.LookupEnv("MISSING")    // "", false

os.Exit — stop without running deferred functions

Exit skips all defers. Prefer returning an error from main's helper to let defers run.

if err := run(); err != nil {
    fmt.Fprintln(os.Stderr, err)
    os.Exit(1)
}

Files — open, create, read, write

ReadFile and WriteFile — the one-shot helpers

Use these when the whole file fits in memory. Nothing to close, nothing to flush.

b, err := os.ReadFile("hello.txt")
if err != nil { log.Fatal(err) }
fmt.Println(string(b))

err = os.WriteFile("out.txt", []byte("hi"), 0644)

os.Open — read-only, streaming

f, err := os.Open("big.log")
if err != nil { log.Fatal(err) }
defer f.Close()

sc := bufio.NewScanner(f)
for sc.Scan() {
    // process line
}

os.Create — write, truncating any existing file

f, err := os.Create("out.log")
if err != nil { log.Fatal(err) }
defer f.Close()
fmt.Fprintln(f, "hello")

os.OpenFile — full control over flags and mode

Combine flags with |. O_APPEND|O_CREATE|O_WRONLY is the classic 'append-only log' pattern.

f, err := os.OpenFile(
    "app.log",
    os.O_APPEND|os.O_CREATE|os.O_WRONLY,
    0644,
)
if err != nil { log.Fatal(err) }
defer f.Close()

Stat, existence checks

os.Stat and FileInfo

fi, err := os.Stat("hello.txt")
if err != nil { log.Fatal(err) }
fmt.Println(fi.Name(), fi.Size(), fi.IsDir(), fi.Mode())

Check for not-exists

errors.Is(err, os.ErrNotExist) is the idiomatic check — it also matches *PathError wrappers.

_, err := os.Stat("missing")
if errors.Is(err, os.ErrNotExist) {
    fmt.Println("nope")
}

Directories

Mkdir vs MkdirAll

Mkdir errors if parents are missing. MkdirAll is idempotent and creates any missing parents.

os.Mkdir("one", 0755)                   // fails if "one" exists
os.MkdirAll("a/b/c", 0755)               // creates whole chain, no error if present

ReadDir — list a directory

entries, _ := os.ReadDir(".")
for _, e := range entries {
    fmt.Println(e.Name(), e.IsDir())
}

Remove vs RemoveAll

Remove works on a single file or empty dir. RemoveAll recurses. Use with extreme care.

os.Remove("file.txt")
os.RemoveAll("temp/")

TempDir / CreateTemp / MkdirTemp

dir, _ := os.MkdirTemp("", "myapp-*")
defer os.RemoveAll(dir)

f, _ := os.CreateTemp(dir, "data-*.json")
defer f.Close()
fmt.Println(f.Name())

Signals

Notify on SIGINT/SIGTERM for graceful shutdown

ctx, stop := signal.NotifyContext(context.Background(),
    os.Interrupt, syscall.SIGTERM)
defer stop()

<-ctx.Done()
fmt.Println("shutting down...")