skip to content

Operating System Access

How a Go program meets the operating system: bytes streamed through io.Reader and io.Writer, files written durably, paths kept inside a directory, child processes, and a clean death on SIGTERM.

part ofGo (Golang)overview, primer and where to startread it →
on this pageshow

explore

questions

102 · 6 sections

A bufio.Writer wraps an output file and the last records never appear on disk - why, and what fixes it?

level: juniorimportance: must knowfreq 62%
basics
~20 s

A bufio.Writer holds bytes in memory until its buffer fills, so whatever is still buffered when the program ends is simply lost. Call Flush before the file is closed, and check the error Flush returns.

open as a page

What does io.Copy(dst, src) do in Go, and what do its two return values mean?

level: juniorimportance: must knowfreq 75%
basics
~20 s

io.Copy streams bytes from a source reader to a destination writer through one small reusable buffer, so memory does not grow with the payload. It returns an int64 count of bytes written and the error that stopped the transfer.

open as a page

Why does io.Reader's Read method take a caller-supplied []byte instead of returning the bytes it read?

level: juniorimportance: must knowfreq 72%
basics
~20 s

io.Reader.Read writes into a byte slice the caller supplies, so the caller decides how much memory a stream costs and can reuse one slice for every call. Returning a fresh slice per read would allocate on every call.

open as a page

What does io.ReadAll do, and why is it risky on a stream you do not control?

level: juniorimportance: must knowfreq 52%
basics
~20 s

io.ReadAll reads an io.Reader to end of stream and returns every byte in one slice. It takes no size limit, so a sender that keeps sending can grow that slice until the process runs out of memory.

open as a page

In io.Seeker, what do the whence values io.SeekStart, io.SeekCurrent and io.SeekEnd mean?

level: juniorimportance: must knowfreq 52%
basics
~20 s

The whence value says what the offset is measured from: io.SeekStart from the beginning of the file, io.SeekCurrent from the position you are at now, io.SeekEnd from the end. Seek returns the new absolute offset.

open as a page

Why write a config file to a temp file and os.Rename it onto the target instead of overwriting in place?

level: juniorimportance: must knowfreq 48%
basics
~20 s

Overwriting in place leaves the file half-written, so a concurrent reader can load a truncated config. Writing a complete temp file first and calling os.Rename swaps the name in one step: readers see the whole old file or the whole new one.

open as a page

What does os.ReadFile give you, and what work does it do that you would otherwise write by hand?

level: juniorimportance: must knowfreq 80%
basics
~20 s

os.ReadFile opens the named file, reads it to the end, closes it, and returns the whole contents as a []byte together with an error. Reaching end of file is not an error, so a successful read returns a nil error.

open as a page

What does Go's os.Stat return, and what can you read from the fs.FileInfo it gives you?

level: juniorimportance: must knowfreq 62%
basics
~20 s

os.Stat returns an fs.FileInfo for a named path, plus an error. FileInfo is an interface: Name, Size, Mode, ModTime, IsDir and Sys report the base name, byte size, mode bits, modification time and whether the name is a directory.

open as a page

In Go, what is the difference between os.Open, os.Create and os.OpenFile?

level: juniorimportance: must knowfreq 70%
basics
~10 s

os.Open opens an existing file read-only. os.Create creates or truncates a file for read-write with mode 0666. os.OpenFile is the general form behind both: you pass the open flags and the permission bits yourself.

open as a page

What does os.ReadDir return, and how do you tell whether an entry is a subdirectory?

level: juniorimportance: must knowfreq 68%
basics
~10 s

os.ReadDir returns a sorted slice of fs.DirEntry values for one directory plus an error. Each entry gives Name, IsDir and Type; IsDir reports a subdirectory. It reads a single level and never recurses.

open as a page

Why does Go code build file paths with filepath.Join instead of concatenating strings with a slash?

level: juniorimportance: must knowfreq 70%
basics
~20 s

filepath.Join separates elements with the platform's own separator, drops empty elements, and cleans the result, so doubled separators and .. segments collapse. Hand-concatenating with a slash yields names like logs//app and emits the wrong separator on Windows.

open as a page

What does the //go:embed directive do, and what must be true of the declaration it sits on?

level: juniorimportance: must knowfreq 58%
basics
~20 s

The //go:embed directive copies the files matching its pattern into the compiled binary at build time. It must sit directly above one package-level variable of type string, []byte, or embed.FS, and the source file must import the embed package.

open as a page

Why does filepath.Join("/srv/uploads", name) not guarantee a path inside /srv/uploads?

level: juniorimportance: must knowfreq 55%
basics
~10 s

filepath.Join cleans its result, so ".." elements are resolved rather than rejected: Join("/srv/uploads", "../../etc/passwd") is "/etc/passwd". Join is path arithmetic, not a confinement check, and Clean is not one either.

open as a page

Do filepath.Clean and filepath.Rel touch the filesystem, and how is filepath.Abs different?

level: middleimportance: should knowfreq 36%
basics
~20 s

No. Clean, Rel, Join, Base, Dir and Ext are pure string operations that never open a file or resolve a symbolic link. filepath.Abs is the exception: for a relative input it prepends the process's working directory.

open as a page

When should you use Go's path package instead of path/filepath?

level: middleimportance: should knowfreq 50%
basics
~20 s

Use path for strings that are slash-separated on every platform: URL paths, archive entry names, slash-separated keys. Use path/filepath only for names you hand to the operating system, because it follows the local separator and understands Windows volume names.

open as a page

In Go, how do you let an environment variable set a flag's default so an explicit flag still wins?

level: juniorimportance: must knowfreq 60%
basics
~20 s

Read the variable with os.LookupEnv before flag.Parse and pass its value as the default argument when you declare the flag. flag.Parse then overwrites that default only for flags that actually appeared on the command line.

open as a page

In Go, what is the difference between os.Getenv and os.LookupEnv?

level: juniorimportance: must knowfreq 70%
basics
~10 s

os.Getenv returns only the value, so an unset variable and one set to the empty string both come back as "". os.LookupEnv returns the value plus a presence boolean, keeping those two cases distinct.

open as a page

In Go's flag package, why does flag.String return a *string, and when may you read it?

level: juniorimportance: must knowfreq 70%
basics
~20 s

flag.String registers a flag before the command line has been read, so it can only hand back a pointer to storage it will fill in later. Call flag.Parse() first, then read the value as *ptr.

open as a page

How do you implement git-style subcommands in Go with flag.NewFlagSet, each owning its own flags?

level: middleimportance: must knowfreq 55%
basics
~10 s

Give each subcommand its own *flag.FlagSet from flag.NewFlagSet, switch on os.Args[1] to choose one, and call that set's Parse on os.Args[2:]. Each set owns its own flag names, usage text and operands.

open as a page

Why is calling os.Chdir inside a Go library or long-running server risky?

level: middleimportance: should knowfreq 45%
basics
~10 s

The working directory is one attribute of the whole process, not something each goroutine owns. os.Chdir changes how every relative path in the program resolves, including paths other goroutines are opening at that instant.

open as a page

What does exec.CommandContext do to the running child process when its context is cancelled?

level: juniorimportance: must knowfreq 52%
basics
~10 s

By default it kills the child outright with os.Process.Kill, which is SIGKILL on Unix, as soon as the context is done. No graceful signal, no grace period, and the program must still call cmd.Wait.

open as a page

Why does exec.Command("ls", "*.go") in Go pass the glob to ls literally instead of expanding it?

level: juniorimportance: must knowfreq 52%
basics
~20 s

exec.Command starts the program directly through the operating system, with no shell involved. Every string after the program name becomes one exact argument, so ls receives the four characters *.go as text. Globbing, pipes, redirection and variable expansion are shell features.

open as a page

In Go's os/exec, how do cmd.Run, cmd.Output and cmd.CombinedOutput differ in what they capture?

level: middleimportance: must knowfreq 58%
basics
~20 s

Run just waits for the child and sends its output wherever Cmd.Stdout and Cmd.Stderr point, discarding it if they are nil. Output returns stdout as bytes. CombinedOutput returns stdout and stderr interleaved in one byte slice.

open as a page

In os/exec, what goes wrong if you call cmd.Wait() before finishing reads from cmd.StdoutPipe()?

level: middleimportance: must knowfreq 52%
basics
~20 s

cmd.Wait closes the pipes it created once the child exits, so unfinished reads lose whatever was still in flight and then fail against a closed file. Read the pipe to end of file first, then call cmd.Wait.

open as a page

In os/exec, when should you use cmd.StdoutPipe() instead of assigning a bytes.Buffer to cmd.Stdout?

level: juniorimportance: should knowfreq 40%
basics
~10 s

Use cmd.StdoutPipe when you must stream output as it arrives, or when the output is too large to hold in memory. Assigning a bytes.Buffer to cmd.Stdout is simpler: os/exec drains the child for you.

open as a page

How does a Go program catch Ctrl-C (SIGINT) instead of exiting immediately?

level: juniorimportance: must knowfreq 68%
basics
~20 s

Call signal.Notify with a buffered os.Signal channel listing os.Interrupt. Go then delivers Ctrl-C on that channel instead of terminating the process, so your code decides what happens next. signal.NotifyContext does the same registration and cancels a context.

open as a page

Why does os.Exit skip every deferred call in a Go program, and what is lost?

level: middleimportance: must knowfreq 60%
basics
~10 s

os.Exit ends the process immediately at the runtime level without unwinding any goroutine's stack, so no deferred call anywhere runs. Whatever a deferred flush, close, cleanup or summary would have done simply never happens.

open as a page

Why does a drain budget built with context.WithTimeout(sigCtx, ...) fire instantly when sigCtx is an already-cancelled signal.NotifyContext context?

level: middleimportance: must knowfreq 55%
basics
~20 s

Cancellation flows from parent to child. A context derived from an already-cancelled parent is born cancelled: its Done channel is closed at once and its Err is context.Canceled, not context.DeadlineExceeded. Parent the budget on a live context.

open as a page

In Go, what exit status does a process report when main returns, and how do you exit non-zero?

level: juniorimportance: should knowfreq 50%
basics
~20 s

When main returns, the Go runtime ends the process with status 0, which every caller reads as success. To report failure you must call os.Exit with a non-zero code; os.Exit(1) is the conventional failure status.

open as a page

Why does a Go worker's shutdown path wait for in-flight work under a context.WithTimeout budget instead of waiting indefinitely?

level: juniorimportance: should knowfreq 45%
basics
~20 s

The platform that sent the stop signal kills the process after a fixed grace period anyway. A bounded wait finishes what it can and then reports what it abandoned; an unbounded wait hands that decision to a kill instead.

open as a page