Day 031 — os, Files & Exit Codes¶
Month 2 · Week 1 · ⬅ Day 030 · Day 032 ➡ · Journal index
🎯 Learning Objective¶
Open, read, write, and close files with the os package, handle the (*os.File, error) idiom, and control process exit codes and arguments the idiomatic way.
📚 Topics¶
os.Open/os.Create/os.OpenFile,defer f.Close()os.ReadFile/os.WriteFilefor whole-file convenienceos.Stdin/os.Stdout/os.Stderr,os.Args,os.Getenvos.Exitand thedefer-doesn't-run trap
📖 Reading / Sources¶
-
ospackage docs - Go blog — Errors are values
- The Go Programming Language §1.5–1.6 (files & args)
📝 Notes¶
os.Open(name)opens read-only and returns(*os.File, error);os.Create(name)opens write-only, truncating/creating with mode0666. For full control useos.OpenFile(name, flag, perm)with flags likeos.O_RDWR|os.O_APPEND|os.O_CREATE→ [[os-file]].- An
*os.Fileis both anio.Readerandio.Writer, so everything from Day 029–030 (io.Copy,bufio.Scanner) works directly on files. - Always
defer f.Close()right after a successful open — but only after checking the error, becausefisnilon failure. For write files, a failingClosecan mean lost data, so on critical writes check theCloseerror explicitly rather than only deferring. os.ReadFile(name)returns the whole file as[]byte;os.WriteFile(name, data, perm)writes it in one call. These replaced the oldioutilversions and are perfect for small files.- The three standard streams are package vars:
os.Stdin,os.Stdout,os.Stderr— all*os.File. Write diagnostics toos.Stderrso they don't pollute piped stdout. os.Argsis[]stringwhereos.Args[0]is the program name; real arg parsing belongs to theflagpackage (later this month).- Exit codes:
os.Exit(code)ends the process immediately with that status.0= success; non-zero = failure.os.Exitdoes NOT run deferred functions — flush/close before calling it, or prefer returning an error up tomain→ [[os-exit-trap]]. - Idiom: keep
mainthin — do work in arun() errorfunction, thenif err := run(); err != nil { fmt.Fprintln(os.Stderr, err); os.Exit(1) }. This letsdefers run insiderunbefore the exit.
💻 Code Examples¶
This day's code has side effects (writing files, calling os.Exit), so it stays
here as a snippet rather than a runnable example:
func main() {
if err := run(); err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
os.Exit(1) // non-zero status; defers in run() already ran
}
}
func run() error {
f, err := os.OpenFile("out.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o644)
if err != nil {
return fmt.Errorf("open: %w", err)
}
defer f.Close() // runs because we return, not os.Exit, from here
if _, err := fmt.Fprintln(f, "appended line"); err != nil {
return fmt.Errorf("write: %w", err)
}
// Whole-file convenience:
data, err := os.ReadFile("out.txt")
if err != nil {
return fmt.Errorf("read: %w", err)
}
fmt.Printf("file now has %d bytes\n", len(data))
return nil
}
🏋️ Exercises / Practice¶
| Exercise | Status | Link |
|---|---|---|
(Reuse) linestats works on os.Open'd files via io.Reader |
✅ | exercises/month-02/week-1/linestats |
🐛 Mistakes Made¶
- Called
os.Exit(1)from deep in the code and was surprised mydefer f.Close()never ran — moved exits tomain. - Deferred
f.Close()before checking theos.Openerror → would have calledCloseon anilfile on the error path.
❓ Open Questions¶
- When should I use
os.OpenFilewith explicit flags vs. theos.Create/os.ReadFileconveniences?
🧠 Active Recall (answer without looking)¶
-
Q: Why can
os.Exit(1)cause data loss in a program usingbufio.Writer?
A
`os.Exit` terminates immediately and runs **no** deferred functions, so a `defer w.Flush()` never executes and the buffered bytes are lost. Flush before exiting, or return an error to `main`. -
Q: What's the value and type of
os.Args[0]?
A
It's a `string` — the program/command name as invoked. Actual arguments start at `os.Args[1]`.
🪶 Feynman Reflection¶
A file handle is just a labeled pipe the OS hands you; an *os.File is both a faucet (Reader) and a drain (Writer), so all my stream tools work on it. The one landmine is os.Exit: it's an emergency stop that skips all the cleanup defer promised, so I keep exits at the very top of the program where there's nothing left to clean up.
🕳️ Knowledge Gaps¶
- File permission bits and umask interaction;
os.Stat/os.FileInfodetails.
✅ Summary¶
I can open/read/write/close files idiomatically, route output to the right stream, read args/env, and control exit codes without losing buffered data.
⏭️ Next Steps / Prep for Tomorrow¶
- Day 032:
fmtverbs deep dive — formatting everything precisely.
| Time spent | Difficulty | Confidence |
|---|---|---|
| 90 min | 🟦🟦⬜⬜⬜ | 🟦🟦🟦⬜⬜ |
Suggested commit: docs(journal): os, files, and exit codes (day 031)