Go os Package

Environment variables, process info, file operations, signals, and running external commands.

Getenv / Setenv Args ReadFile / WriteFile Open / Create Stat / MkdirAll Exit os/exec
๐ŸŒฟ

Environment Variables

๐Ÿ’ก
Use os.LookupEnv instead of os.Getenv when you need to distinguish between a variable that is unset vs. one that is set to an empty string.
Getenv, LookupEnv, Setenv, Unsetenv Env
// Getenv โ€” empty string if not set (can't distinguish unset vs "")
host := os.Getenv("DB_HOST")

// LookupEnv โ€” second return tells you if the key exists
port, ok := os.LookupEnv("DB_PORT")
if !ok {
    port = "5432"  // use default
}

// Setenv / Unsetenv
os.Setenv("APP_ENV", "production")
os.Unsetenv("APP_ENV")

// Environ โ€” all env vars as "KEY=VALUE" strings
for _, e := range os.Environ() {
    fmt.Println(e)  // PATH=/usr/bin:..., HOME=/Users/alice, ...
}

// Clearenv โ€” remove all environment variables
os.Clearenv()
โš™๏ธ

Process Info

Args and process identity Process
// os.Args โ€” command-line arguments
// Args[0] is the program name
fmt.Println(os.Args[0])  // ./myapp
fmt.Println(os.Args[1:]) // remaining args

os.Getpid()  // current process ID
os.Getppid() // parent process ID
os.Getuid()  // numeric user ID
os.Getgid()  // numeric group ID

hostname, _ := os.Hostname()
wd, _       := os.Getwd()  // working directory
Stdin, Stdout, Stderr and Exit Streams
// Standard streams are *os.File values
fmt.Fprintln(os.Stdout, "to stdout")
fmt.Fprintln(os.Stderr, "error output")

// Read a line from stdin
scanner := bufio.NewScanner(os.Stdin)
if scanner.Scan() {
    line := scanner.Text()
    fmt.Println("got:", line)
}

// Exit โ€” immediately terminates the process
// defer statements do NOT run before os.Exit
os.Exit(0)  // success
os.Exit(1)  // failure
๐Ÿ“„

Reading & Writing Files

โ„น๏ธ
os.ReadFile and os.WriteFile are convenience wrappers that handle open/read/close in one call. Use os.Open / os.Create when you need streaming, seeking, or finer control.
ReadFile โ€” read entire file at once ReadFile
// ReadFile reads and closes in one call
data, err := os.ReadFile("config.json")
if err != nil {
    log.Fatal(err)
}
fmt.Println(string(data))

// Combine with JSON decode
var cfg Config
data, _ = os.ReadFile("config.json")
json.Unmarshal(data, &cfg)
WriteFile โ€” write bytes to a file WriteFile
// WriteFile creates or truncates, then writes
content := []byte("Hello, World!\n")
err := os.WriteFile("output.txt", content, 0644)

// Common pattern: marshal then write
data, _ := json.MarshalIndent(cfg, "", "  ")
os.WriteFile("config.json", data, 0644)

// File permissions (Unix octal)
// 0644 โ€” owner rw, group r, others r (files)
// 0755 โ€” owner rwx, group rx, others rx (dirs/executables)
Open and Create โ€” streaming file access Open / Create
// Open โ€” read-only
f, err := os.Open("data.txt")
if err != nil {
    log.Fatal(err)
}
defer f.Close()
// use f as an io.Reader (bufio.Scanner, json.Decoder, etc.)

// Create โ€” create or truncate for writing
out, err := os.Create("output.txt")
if err != nil {
    log.Fatal(err)
}
defer out.Close()
fmt.Fprintln(out, "line 1")
fmt.Fprintln(out, "line 2")

// OpenFile โ€” full control over flags and permissions
f2, err := os.OpenFile("log.txt",
    os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
defer f2.Close()
fmt.Fprintln(f2, "appended line")
OpenFile flags reference Flags
os.O_RDONLY  // read-only (default for Open)
os.O_WRONLY  // write-only
os.O_RDWR   // read+write
os.O_APPEND  // append on each write
os.O_CREATE  // create if not exists
os.O_EXCL    // error if file exists (with O_CREATE)
os.O_TRUNC   // truncate to zero on open
os.O_SYNC    // synchronous I/O

// Combine with bitwise OR
// Read+write, create if absent, don't truncate:
flags := os.O_RDWR | os.O_CREATE
๐Ÿ“

File System Operations

Stat โ€” inspect a file or directory Stat
info, err := os.Stat("data.txt")
if err != nil {
    if os.IsNotExist(err) {
        fmt.Println("file does not exist")
    }
}

info.Name()     // "data.txt"
info.Size()     // 1024 (bytes)
info.Mode()     // -rw-r--r-- (FileMode)
info.ModTime()  // time.Time
info.IsDir()    // false

// Lstat โ€” like Stat but doesn't follow symlinks
info2, _ := os.Lstat("link.txt")
Mkdir, MkdirAll, Remove, RemoveAll Dirs
// Mkdir โ€” create one directory (parent must exist)
os.Mkdir("logs", 0755)

// MkdirAll โ€” create full path, like mkdir -p
os.MkdirAll("data/2024/reports", 0755)

// Remove โ€” delete a file or empty directory
os.Remove("temp.txt")

// RemoveAll โ€” delete a path and all its contents
os.RemoveAll("data/cache")

// Rename โ€” move or rename
os.Rename("old.txt", "new.txt")
os.Rename("file.txt", "archive/file.txt")
ReadDir โ€” list directory contents ReadDir
// ReadDir returns entries sorted by name
entries, err := os.ReadDir(".")
if err != nil {
    log.Fatal(err)
}
for _, e := range entries {
    fmt.Printf("%s  dir=%v\n", e.Name(), e.IsDir())
}

// Get full FileInfo if needed
for _, e := range entries {
    info, _ := e.Info()
    fmt.Printf("%-20s %6d bytes\n", e.Name(), info.Size())
}

// Temp files and directories
f, _ := os.CreateTemp("", "prefix-*.txt")
defer os.Remove(f.Name())

dir, _ := os.MkdirTemp("", "workdir-*")
defer os.RemoveAll(dir)
โŒ

Error Checking

โš ๏ธ
Prefer errors.Is(err, fs.ErrNotExist) over os.IsNotExist(err) for wrapped errors. The os.Is* helpers work on the raw error only.
Checking specific OS error types Errors
import (
    "errors"
    "io/fs"
)

_, err := os.Open("missing.txt")

// Modern style โ€” works through wrapped errors
errors.Is(err, fs.ErrNotExist)    // true
errors.Is(err, fs.ErrPermission)  // false
errors.Is(err, fs.ErrExist)       // false

// Legacy helpers (don't unwrap)
os.IsNotExist(err)    // true
os.IsExist(err)       // true when creating an already-existing file
os.IsPermission(err)  // true for EACCES / EPERM
os.IsTimeout(err)     // true for timeout errors

// Extract the underlying *PathError for details
var pe *os.PathError
if errors.As(err, &pe) {
    fmt.Println(pe.Op, pe.Path, pe.Err)
    // open missing.txt no such file or directory
}
๐Ÿš€

Running Commands โ€” os/exec

โš ๏ธ
Never construct shell commands by concatenating user input โ€” that's a command injection vulnerability. Pass arguments as separate strings to exec.Command and they're never interpreted by a shell.
exec.Command โ€” capture output Output
import "os/exec"

// Output โ€” run and capture stdout; returns error if exit code != 0
out, err := exec.Command("git", "rev-parse", "HEAD").Output()
if err != nil {
    log.Fatal(err)
}
fmt.Println(strings.TrimSpace(string(out)))

// CombinedOutput โ€” capture stdout + stderr together
out2, err := exec.Command("go", "build", "./...").CombinedOutput()
if err != nil {
    fmt.Fprintf(os.Stderr, "build failed:\n%s\n", out2)
}

// Run โ€” just run, no output capture
err = exec.Command("go", "fmt", "./...").Run()
Cmd struct โ€” full control over streams and env Cmd
cmd := exec.Command("ffmpeg", "-i", "input.mp4", "output.mp3")

// Pipe stdout/stderr to our own writers
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

// Provide stdin
cmd.Stdin = strings.NewReader("input data")

// Set working directory
cmd.Dir = "/tmp/work"

// Override or extend environment
cmd.Env = append(os.Environ(), "GOFLAGS=-mod=vendor")

if err := cmd.Run(); err != nil {
    var exitErr *exec.ExitError
    if errors.As(err, &exitErr) {
        fmt.Println("exit code:", exitErr.ExitCode())
    }
}
Start + Wait โ€” async execution Async
// Start begins the command; Wait blocks until done
cmd := exec.Command("sleep", "5")
if err := cmd.Start(); err != nil {
    log.Fatal(err)
}
fmt.Println("pid:", cmd.Process.Pid)

// do other work here ...

if err := cmd.Wait(); err != nil {
    log.Fatal(err)
}
LookPath โ€” find a binary LookPath
// LookPath resolves a binary name to a full path
path, err := exec.LookPath("git")
if err != nil {
    log.Fatal("git not found")
}
fmt.Println(path) // /usr/bin/git

// Check availability without running
_, err = exec.LookPath("docker")
dockerAvailable := err == nil
๐Ÿ“‹

Quick Reference

FunctionReturnsDescription
os.Getenv(key)stringValue of env var; empty if unset
os.LookupEnv(key)string, boolValue and whether the key exists
os.Setenv(key, val)errorSet an environment variable
os.Unsetenv(key)errorDelete an environment variable
os.Environ()[]stringAll env vars as "KEY=VALUE"
os.Args[]stringCommand-line arguments; [0] is program name
os.Getpid()intCurrent process ID
os.Getwd()string, errorCurrent working directory
os.Hostname()string, errorMachine hostname
os.Exit(code)โ€”Terminate process; defers do NOT run
os.ReadFile(name)[]byte, errorRead entire file
os.WriteFile(name, data, perm)errorWrite/overwrite entire file
os.Open(name)*File, errorOpen file for reading
os.Create(name)*File, errorCreate or truncate for writing
os.OpenFile(name, flag, perm)*File, errorOpen with custom flags
os.Stat(name)FileInfo, errorFile metadata (size, mode, modtime)
os.Lstat(name)FileInfo, errorLike Stat but doesn't follow symlinks
os.Mkdir(name, perm)errorCreate a single directory
os.MkdirAll(path, perm)errorCreate full directory path (mkdir -p)
os.Remove(name)errorDelete file or empty directory
os.RemoveAll(path)errorDelete path and all contents
os.Rename(old, new)errorMove or rename a file
os.ReadDir(name)[]DirEntry, errorList directory contents, sorted
os.CreateTemp(dir, pattern)*File, errorCreate a unique temp file
os.MkdirTemp(dir, pattern)string, errorCreate a unique temp directory
os.IsNotExist(err)boolTrue if error is "file not found"
os.IsPermission(err)boolTrue if error is permission denied
Function (os/exec)ReturnsDescription
exec.Command(name, args...)*CmdCreate a Cmd; does not start it
cmd.Output()[]byte, errorRun and capture stdout
cmd.CombinedOutput()[]byte, errorRun and capture stdout + stderr
cmd.Run()errorRun and wait; no output capture
cmd.Start()errorStart without waiting
cmd.Wait()errorWait for a started command to finish
exec.LookPath(file)string, errorFind binary in PATH