PPrepLearn
Progress · 0/13 sections

01 — OS Interaction & I/O Operations

8 min read

01 — OS Interaction & I/O Operations

File system, process management, signals, environment, io interfaces


os Package — File Operations

File CRUD

import "os"
 
// Create / Write
f, err := os.Create("output.txt")           // create or truncate
f.WriteString("Hello, Go!\n")
f.Write([]byte("more data"))
f.Close()
 
// Write entire file at once
os.WriteFile("config.txt", []byte("data"), 0644)
 
// Read entire file
data, err := os.ReadFile("config.txt")
 
// Open for reading
f, err := os.Open("config.txt")  // read-only
defer f.Close()
 
// Open with flags
f, err := os.OpenFile("log.txt",
    os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
 
// Delete
os.Remove("file.txt")        // single file
os.RemoveAll("directory/")   // recursive

File Flags

FlagPurpose
os.O_RDONLYRead only
os.O_WRONLYWrite only
os.O_RDWRRead+Write
os.O_CREATECreate if not exists
os.O_APPENDAppend to file
os.O_TRUNCTruncate file
os.O_EXCLError if file exists
os.O_SYNCSynchronous I/O

File Info

info, err := os.Stat("file.txt")
if os.IsNotExist(err) {
    fmt.Println("File doesn't exist!")
}
 
fmt.Println("Name:", info.Name())
fmt.Println("Size:", info.Size())
fmt.Println("Mode:", info.Mode())
fmt.Println("ModTime:", info.ModTime())
fmt.Println("IsDir:", info.IsDir())

Directory Operations

// Create directory
os.Mkdir("dir", 0755)
os.MkdirAll("path/to/nested/dir", 0755)  // recursive
 
// Read directory
entries, _ := os.ReadDir(".")
for _, entry := range entries {
    info, _ := entry.Info()
    fmt.Printf("%s\t%d\t%s\n", entry.Name(), info.Size(), 
        map[bool]string{true: "DIR", false: "FILE"}[entry.IsDir()])
}
 
// Change directory
os.Chdir("/tmp")
cwd, _ := os.Getwd()
 
// Temp file/dir
f, _ := os.CreateTemp("", "prefix-*.txt")  // "" = os temp dir
dir, _ := os.MkdirTemp("", "myapp-*")

File Permissions

// Chmod
os.Chmod("file.txt", 0644)
 
// Chown (Unix only)
os.Chown("file.txt", uid, gid)
 
// Permission bits:
// 0644 = rw-r--r--  (owner: rw, group: r, others: r)
// 0755 = rwxr-xr-x  (owner: rwx, group: rx, others: rx)
// 0600 = rw-------  (owner only)
os.Symlink("target.txt", "link.txt")     // symbolic link
os.Link("target.txt", "hardlink.txt")    // hard link
target, _ := os.Readlink("link.txt")     // read symlink target
 
// Lstat doesn't follow symlinks
info, _ := os.Lstat("link.txt")          // info about the link itself

os/exec — External Commands

import "os/exec"
 
// Simple command
cmd := exec.Command("ls", "-la", "/tmp")
output, err := cmd.Output()  // stdout only
fmt.Println(string(output))
 
// Combined output (stdout + stderr)
output, err := cmd.CombinedOutput()
 
// Separate stdout/stderr
var stdout, stderr bytes.Buffer
cmd := exec.Command("git", "status")
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
 
// Start + Wait (non-blocking)
cmd := exec.Command("sleep", "5")
cmd.Start()  // starts process, doesn't wait
// ... do other stuff ...
cmd.Wait()   // now wait for completion
 
// With context (timeout/cancellation)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
cmd := exec.CommandContext(ctx, "long-running-task")
output, err := cmd.Output()
 
// Environment
cmd := exec.Command("myapp")
cmd.Env = append(os.Environ(), "MY_VAR=value")
cmd.Dir = "/working/directory"
 
// Stdin pipe
cmd := exec.Command("wc", "-l")
cmd.Stdin = strings.NewReader("line1\nline2\nline3\n")
output, _ := cmd.Output()
// "3"
 
// Interactive stdin/stdout pipes
cmd := exec.Command("cat")
stdin, _ := cmd.StdinPipe()
stdout, _ := cmd.StdoutPipe()
cmd.Start()
stdin.Write([]byte("hello\n"))
stdin.Close()
io.Copy(os.Stdout, stdout)
cmd.Wait()

LookPath

// Find executable in PATH
path, err := exec.LookPath("python3")
fmt.Println(path)  // /usr/bin/python3

os/signal — Signal Handling

import (
    "os"
    "os/signal"
    "syscall"
)
 
// Catch signals
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, 
    os.Interrupt,      // Ctrl+C (SIGINT)
    syscall.SIGTERM,   // kill command
    syscall.SIGHUP,    // terminal hangup
)
 
go func() {
    sig := <-sigCh
    fmt.Println("Received signal:", sig)
    // cleanup...
    os.Exit(0)
}()
 
// Stop receiving signals
signal.Stop(sigCh)
 
// Reset signal to default behavior
signal.Reset(os.Interrupt)
 
// Context-based (Go 1.16+)
ctx, stop := signal.NotifyContext(context.Background(), 
    os.Interrupt, syscall.SIGTERM)
defer stop()
 
// ctx.Done() will be closed on signal
<-ctx.Done()
fmt.Println("Shutting down...")

Common Signals

SignalNumberDefaultUse
SIGINT2TerminateCtrl+C
SIGTERM15Terminatekill command
SIGHUP1TerminateConfig reload
SIGUSR110TerminateCustom
SIGUSR212TerminateCustom
SIGKILL9KillCannot catch!
SIGSTOP19StopCannot catch!

Environment Variables

// Get
value := os.Getenv("HOME")
value, exists := os.LookupEnv("MY_VAR")
 
// Set
os.Setenv("MY_VAR", "hello")
 
// Unset
os.Unsetenv("MY_VAR")
 
// All environment
for _, env := range os.Environ() {
    parts := strings.SplitN(env, "=", 2)
    fmt.Printf("%s = %s\n", parts[0], parts[1])
}
 
// Clear all
os.Clearenv()
 
// Expand variables in string
path := os.ExpandEnv("$HOME/.config/${APP_NAME}")

os/user — User Information

import "os/user"
 
// Current user
u, _ := user.Current()
fmt.Println(u.Username)  // "ashutosh"
fmt.Println(u.HomeDir)   // "/home/ashutosh"
fmt.Println(u.Uid)       // "1000"
fmt.Println(u.Gid)       // "1000"
 
// Lookup by name
u, _ = user.Lookup("root")
 
// Lookup by ID
u, _ = user.LookupId("0")
 
// Group lookup
g, _ := user.LookupGroup("sudo")
fmt.Println(g.Gid, g.Name)

io Package — Core Interfaces

Fundamental Interfaces

// Reader — read bytes from a source
type Reader interface {
    Read(p []byte) (n int, err error)
}
 
// Writer — write bytes to a destination
type Writer interface {
    Write(p []byte) (n int, err error)
}
 
// Closer — release resources
type Closer interface {
    Close() error
}
 
// Seeker — random access
type Seeker interface {
    Seek(offset int64, whence int) (int64, error)
}
 
// Combined interfaces
type ReadWriter interface { Reader; Writer }
type ReadCloser interface { Reader; Closer }
type WriteCloser interface { Writer; Closer }
type ReadWriteCloser interface { Reader; Writer; Closer }
type ReadSeeker interface { Reader; Seeker }
type ReadWriteSeeker interface { Reader; Writer; Seeker }

io Utility Functions

import "io"
 
// Copy all data
n, err := io.Copy(dst, src)           // reads until EOF
n, err := io.CopyN(dst, src, 1024)    // copy exactly N bytes
n, err := io.CopyBuffer(dst, src, buf) // with custom buffer
 
// Read all
data, err := io.ReadAll(reader)
 
// Read exactly N bytes
buf := make([]byte, 100)
_, err := io.ReadFull(reader, buf)  // error if < 100 bytes
 
// ReadAtLeast
_, err := io.ReadAtLeast(reader, buf, 50) // at least 50 bytes
 
// Write string
io.WriteString(writer, "hello")
 
// Discard reader output
io.Copy(io.Discard, reader)
 
// Pipe — in-memory io.Reader ↔ io.Writer
pr, pw := io.Pipe()
go func() {
    pw.Write([]byte("hello from writer"))
    pw.Close()
}()
data, _ := io.ReadAll(pr)
 
// MultiReader — concatenate readers
r := io.MultiReader(reader1, reader2, reader3)
 
// MultiWriter — tee (write to multiple writers)
w := io.MultiWriter(file, os.Stdout, &buf)
 
// TeeReader — read and copy simultaneously
tee := io.TeeReader(reader, &logBuf)
io.Copy(destination, tee)  // data goes to both destination AND logBuf
 
// LimitReader — limit how much can be read
limited := io.LimitReader(reader, 1024)  // max 1024 bytes
 
// SectionReader — read a section of ReadAt
section := io.NewSectionReader(readerAt, offset, length)
 
// NopCloser — add Close() to a Reader
rc := io.NopCloser(reader)  // returns ReadCloser

io/fs — File System Interface (Go 1.16+)

import "io/fs"
 
// Walk directory tree
fs.WalkDir(os.DirFS("."), ".", func(path string, d fs.DirEntry, err error) error {
    if err != nil { return err }
    fmt.Println(path)
    return nil
})
 
// Glob pattern matching
matches, _ := fs.Glob(os.DirFS("."), "*.go")
 
// Read file from FS
data, _ := fs.ReadFile(myFS, "config.json")
 
// Sub filesystem
sub, _ := fs.Sub(myFS, "templates")
 
// Embedded files
//go:embed templates/*
var templateFS embed.FS
 
// Use with http
http.Handle("/", http.FileServer(http.FS(templateFS)))

bufio — Buffered I/O

import "bufio"
 
// Buffered Reader
reader := bufio.NewReader(file)
reader = bufio.NewReaderSize(file, 64*1024) // 64KB buffer
 
// Read line
line, err := reader.ReadString('\n')
 
// Scanner (line by line — most common)
scanner := bufio.NewScanner(file)
scanner.Buffer(make([]byte, 1024*1024), 1024*1024) // max 1MB lines
for scanner.Scan() {
    fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
    log.Fatal(err)
}
 
// Custom split function
scanner.Split(bufio.ScanWords)  // word by word
scanner.Split(bufio.ScanRunes)  // rune by rune
scanner.Split(bufio.ScanBytes)  // byte by byte
 
// Custom splitter
scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) {
    // split on double newline
    if i := bytes.Index(data, []byte("\n\n")); i >= 0 {
        return i + 2, data[:i], nil
    }
    if atEOF && len(data) > 0 {
        return len(data), data, nil
    }
    return 0, nil, nil
})
 
// Buffered Writer
writer := bufio.NewWriter(file)
writer.WriteString("hello\n")
writer.Flush()  // MUST flush before closing!

path/filepath — Path Manipulation

import "path/filepath"
 
// Join paths (OS-aware separators)
p := filepath.Join("home", "user", "docs", "file.txt")
// Linux: "home/user/docs/file.txt"
// Windows: "home\\user\\docs\\file.txt"
 
// Split
dir, file := filepath.Split("/home/user/file.txt")
// dir = "/home/user/", file = "file.txt"
 
// Extension
ext := filepath.Ext("photo.jpg")  // ".jpg"
 
// Base name
base := filepath.Base("/path/to/file.txt")  // "file.txt"
 
// Directory
dir := filepath.Dir("/path/to/file.txt")  // "/path/to"
 
// Absolute path
abs, _ := filepath.Abs("relative/path")
 
// Clean path (resolve . and ..)
clean := filepath.Clean("/a/b/../c/./d")  // "/a/c/d"
 
// Relative path
rel, _ := filepath.Rel("/a/b", "/a/b/c/d")  // "c/d"
 
// Match glob
matched, _ := filepath.Match("*.go", "main.go")  // true
 
// Walk directory
filepath.WalkDir(".", func(path string, d fs.DirEntry, err error) error {
    if err != nil { return err }
    if filepath.Ext(path) == ".go" {
        fmt.Println(path)
    }
    return nil
})
 
// Glob
files, _ := filepath.Glob("/tmp/*.log")

Process Info & Control

// Current process
pid := os.Getpid()
ppid := os.Getppid()
 
// Exit
os.Exit(0)  // success
os.Exit(1)  // failure
 
// Command line args
args := os.Args  // [program, arg1, arg2, ...]
 
// Hostname
host, _ := os.Hostname()
 
// Page size
pageSize := os.Getpagesize()
 
// Process state
proc, _ := os.FindProcess(pid)
proc.Signal(syscall.SIGTERM)  // send signal
state, _ := proc.Wait()       // wait for process

Error Types

// Sentinel errors
os.ErrNotExist     // file doesn't exist
os.ErrExist        // file already exists
os.ErrPermission   // permission denied
os.ErrClosed       // file already closed
 
// Check error type
if errors.Is(err, os.ErrNotExist) {
    fmt.Println("File not found!")
}
 
// PathError — detailed error
var pathErr *os.PathError
if errors.As(err, &pathErr) {
    fmt.Printf("Op: %s, Path: %s, Err: %v\n", 
        pathErr.Op, pathErr.Path, pathErr.Err)
}

Related