sysx

package
v0.1.24 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 13, 2026 License: GPL-3.0 Imports: 26 Imported by: 0

README

sysx

sysx is a lightweight, production-grade system utilities toolkit for Go, providing a clean and consistent API for OS detection, runtime introspection, environment variable management, process control, command execution, and file system queries — all built exclusively on the Go standard library.

Overview

The sysx package eliminates the boilerplate of writing low-level system queries from scratch. It addresses common pain points like:

  • OS / Architecture detection – know at runtime whether you are on Linux, macOS, Windows, 64-bit, or ARM
  • Runtime introspection – read hostname, PID, UID, GID, number of CPUs, goroutine count, Go version, and memory stats in a single call
  • Environment management – read, write, and parse environment variables with typed helpers (int, bool, slice) and sensible fallbacks
  • Process utilities – check whether a PID is alive, send signals, look up processes by PID
  • Command execution – a builder API plus convenience functions supporting timeout, working-directory override, environment injection, real-time streaming, async launch, and shell-style pipelines
  • File system helpers – existence checks, type checks, permission checks, size queries, directory lookups, read/write helpers, atomic writes, and concurrency-safe writers

Problem Solved: Querying the operating system involves a patchwork of os, os/exec, runtime, syscall, and os/user calls scattered across many packages. sysx unifies these into a single, coherent API with uniform error handling and well-documented behavior.

Design Philosophy

  • Zero external dependencies — only the Go standard library is used
  • Safe for concurrent use — all exported functions are stateless or read-only with respect to shared state
  • Explicit errors, not silent failures — functions return errors rather than hiding them; Must* variants are provided where panicking on failure is a deliberate choice
  • Platform-aware — differences between Linux, macOS, and Windows are documented and handled gracefully
  • No shell interpolation — command execution helpers use os/exec directly, never sh -c, to avoid injection risks
  • DRY internals — the Command builder is the single implementation; all convenience functions delegate to it

Package Architecture

File Responsibility
type.go Struct definitions (Command, CommandResult, SafeFileWriter) with unexported fields; getter/accessor methods; package-level globals
doc.go Package-level godoc documentation
os.go OS and architecture detection (IsLinux, IsDarwin, IsWindows, OSVersion, …)
runtime.go Runtime information (Hostname, PID, UID, GoVersion, MemStats, …)
env.go Environment variable helpers (Getenv, MustGetenv, Hasenv, typed getters, EnvMap)
process.go Process utilities (ProcessExists, KillProcess, FindProcessByPID, …)
command.go Command builder (NewCommand, With* methods, Execute, Run, Output) and all convenience functions
file.go File system helpers — existence/type/permission checks, read/write functions, atomic and concurrency-safe writers
net.go Network utilities — IP classification, port probing, address resolution, connectivity helpers
utilities.go Internal helpers (parseBoolString, splitLines) and exported UserInfo()
entry.go Top-level convenience (SystemInfo, IsPrivileged)
resource.go Resource builder with chainable With* setters and From* loaders, plus lifecycle methods (Close, Rewind, CopyTo, Drain)
memblob.go MemBlob: in-memory ReadSeekCloser backing for small payloads
tempfile.go TempFile: on-disk temporary file backing with idempotent Close and configurable auto-removal
spillbuf.go Private hybrid memory → spill-file backing used by FromReader
mime.go MimeFromName helper, MIME constants, and ErrNilResource

Installation

go get github.com/sivaosorg/replify

Import the package:

import "github.com/sivaosorg/replify/pkg/sysx"

Requirements: Go 1.24.0 or higher

API Reference

OS Detection (os.go)
sysx.IsLinux()   bool   // true on Linux
sysx.IsDarwin()  bool   // true on macOS
sysx.IsWindows() bool   // true on Windows

sysx.OSName()    string // runtime.GOOS  ("linux", "darwin", "windows", …)
sysx.Arch()      string // runtime.GOARCH ("amd64", "arm64", "386", …)
sysx.Is64Bit()   bool   // true for amd64, arm64, ppc64, …
sysx.IsArm()     bool   // true for arm and arm64

sysx.OSVersion() string // best-effort OS version string

OSVersion resolution:

Platform Source
Linux PRETTY_NAME field from /etc/os-release
macOS Output of sw_vers -productVersion
Windows runtime.GOOS + "/" + runtime.GOARCH
Other runtime.GOOS
Runtime Information (runtime.go)
sysx.Hostname()          (string, error) // os.Hostname()
sysx.MustHostname()      string          // panics on error
sysx.PID()               int             // os.Getpid()
sysx.PPID()              int             // os.Getppid()
sysx.UID()               int             // os.Getuid()  (-1 on Windows)
sysx.GID()               int             // os.Getgid()  (-1 on Windows)
sysx.ExecutablePath()    (string, error) // os.Executable()
sysx.MustExecutablePath() string         // panics on error
sysx.NumCPU()            int             // runtime.NumCPU()
sysx.NumGoroutine()      int             // runtime.NumGoroutine()
sysx.GoVersion()         string          // runtime.Version() — e.g. "go1.24.0"
sysx.MemStats()          runtime.MemStats
Environment Utilities (env.go)
sysx.Getenv(key, fallback string) string          // env var or fallback
sysx.MustGetenv(key string) string                // panics if absent/empty
sysx.Hasenv(key string) bool                      // true when set and non-empty
sysx.Setenv(key, value any) error              // os.Setenv
sysx.Unsetenv(key string) error                   // os.Unsetenv
sysx.GetenvInt(key string, fallback int) int      // parsed int or fallback
sysx.GetenvBool(key string, fallback bool) bool   // parsed bool or fallback
sysx.GetenvSlice(key, sep string) []string        // split by sep, nil if unset
sysx.Environ() []string                           // os.Environ()
sysx.EnvMap() map[string]string                   // all env vars as map

Bool string recognition (case-insensitive):

Truthy Falsy
1, true, yes, on 0, false, no, off
Process Utilities (process.go)
sysx.ProcessExists(pid int) bool                     // true when process is running
sysx.KillProcess(pid int) error                      // SIGTERM
sysx.KillProcessForcefully(pid int) error            // SIGKILL
sysx.CurrentProcessName() string                     // filepath.Base of executable
sysx.FindProcessByPID(pid int) (*os.Process, error)  // os.FindProcess
Command Execution (command.go)

The command subsystem has two API layers that share a single implementation. All CommandResult fields are unexported; always use the accessor methods.

Builder API — Command and CommandResult
// Create and configure a Command:
cmd := sysx.NewCommand("bash").
    WithArgs("-c", "echo hello").
    WithTimeout(5 * time.Second).
    WithEnv("APP_ENV=prod").
    WithDir("/tmp").
    WithStdin(os.Stdin).
    WithStdout(os.Stdout).
    WithStderr(os.Stderr)

// Execute and get a structured result:
res := cmd.Execute()

// Read results via accessor methods:
res.Stdout()   string         // captured stdout (empty when WithStdout was used)
res.Stderr()   string         // captured stderr (empty when WithStderr was used)
res.ExitCode() int            // 0 on success, -1 when undetermined
res.Duration() time.Duration  // wall-clock execution time
res.Err()      error          // nil on success

// Convenience methods on *CommandResult:
res.IsSuccess()   bool   // true when Err() == nil
res.Combined()  string // Stdout() + Stderr()

// Shorter variants on *Command:
cmd.Run()           error           // Execute().Err()
cmd.Output()        (string, error) // Execute().Combined(), Execute().Err()
Command Getters
cmd.Name()     string         // program name or path
cmd.Args()     []string       // positional arguments
cmd.Dir()      string         // working directory
cmd.Env()      []string       // extra KEY=VALUE bindings
cmd.Timeout()  time.Duration  // configured timeout (0 = none)
Convenience Functions
// Structured result shortcut
sysx.RunCommand(name string, args ...string) *CommandResult

// Discard output
sysx.ExecCommand(name string, args ...string) error
sysx.ExecCommandContext(ctx context.Context, name string, args ...string) error
sysx.ExecCommandWithTimeout(timeout time.Duration, name string, args ...string) error
sysx.ExecCommandInDir(dir, name string, args ...string) error

// Capture combined stdout+stderr
sysx.ExecOutput(name string, args ...string) (string, error)
sysx.ExecOutputWithTimeout(timeout time.Duration, name string, args ...string) (string, error)
sysx.ExecOutputInDir(dir, name string, args ...string) (string, error)

// Capture stdout as lines
sysx.ExecOutputLines(name string, args ...string) ([]string, error)

// Stream output in real time to provided writers
sysx.ExecStreaming(stdout, stderr io.Writer, name string, args ...string) error

// Start asynchronously — caller calls cmd.Wait()
sysx.ExecAsync(name string, args ...string) (*exec.Cmd, error)

// Chain commands as a shell pipeline
sysx.ExecPipeline(commands ...[]string) (string, error)
File System Utilities (file.go)
Existence and Type Checks
sysx.FileExists(path string) bool   // any file system entry
sysx.DirExists(path string) bool    // directory only
sysx.IsFile(path string) bool       // regular file (follows symlinks)
sysx.IsDir(path string) bool        // directory (follows symlinks)
sysx.IsSymlink(path string) bool    // symbolic link (does NOT follow)
Permission Checks
sysx.IsExecutable(path string) bool  // owner execute bit (0100)
sysx.IsReadable(path string) bool    // owner read bit    (0400)
sysx.IsWritable(path string) bool    // attempts O_WRONLY open
File Metadata and Directories
sysx.FileSize(path string) (int64, error)
sysx.TempDir() string
sysx.HomeDir() (string, error)
sysx.MustHomeDir() string
sysx.WorkingDir() (string, error)
sysx.MustWorkingDir() string
File Reading
sysx.ReadFile(path string) ([]byte, error)
sysx.ReadFileString(path string) (string, error)
sysx.ReadLines(path string) ([]string, error)
sysx.StreamLines(path string, handler func(string) error) error
File Writing
sysx.WriteFile(path string, data []byte) error
sysx.WriteFileString(path string, content string) error
sysx.AppendFile(path string, data []byte) error
sysx.AppendString(path string, content string) error
sysx.WriteLines(path string, lines []string) error
Atomic and Concurrency-Safe Writes
// Atomic replace via temp-file + rename (POSIX atomic)
sysx.AtomicWriteFile(path string, data []byte) error

// Per-path in-process mutex (multiple goroutines, same path)
sysx.WriteFileLocked(path string, data []byte) error

// Reusable mutex-protected writer for one path
w := sysx.NewSafeFileWriter(path).WithPerm(0o600)
w.Write(data []byte) error
w.WriteString(s string) error
w.Overwrite(data []byte) error  // atomic replace
// Accessors:
w.Path() string        // the file path
w.Perm() os.FileMode   // file permission
Network Utilities (net.go)
IP Classification
sysx.IsIPv4("192.168.1.1")  bool  // true for valid dotted-decimal IPv4
sysx.IsIPv6("::1")          bool  // true for valid IPv6 (non-IPv4)
sysx.IsLocalIP("10.0.0.1")  bool  // true for loopback, link-local, RFC 1918

Recognized local ranges: 127.0.0.0/8, 169.254.0.0/16, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, ::1, fe80::/10, fc00::/7.

Port Probing
sysx.IsPortOpen("db.internal", 5432) bool    // TCP connect attempt, 3s timeout
sysx.IsPortAvailable(8080)           bool    // true when port can be bound
Network Address Helpers
sysx.GetLocalIP()         (string, error)    // first non-loopback IPv4
sysx.GetPublicIP()        (string, error)    // via https://api.ipify.org (needs internet)
sysx.GetInterfaceIPs()    ([]string, error)  // all IPs across all interfaces
URL and Host Helpers
sysx.IsValidHost("localhost")          bool             // DNS resolve or valid IP
sysx.ParseHostPort("host:8080")        (host, port, error)
sysx.IsValidURL("https://example.com") bool             // valid scheme + host
Connectivity
sysx.PingHost("google.com")                       error  // TCP port 80, 5s timeout
sysx.CheckTCPConn("db", 5432, 3*time.Second) error  // any port, custom timeout
Entry-Point Helpers (entry.go)
sysx.SystemInfo() map[string]string
// keys: "os", "arch", "hostname", "pid", "go_version", "executable", "num_cpu"

sysx.IsPrivileged() bool  // true when UID() == 0 (root on Unix)
Utility Helpers (utilities.go)
sysx.UserInfo() string  // "uid=1000 gid=1000"
Resource — Storage-Agnostic Exports (resource.go, memblob.go, tempfile.go, spillbuf.go, mime.go)

Resource is the storage-agnostic envelope every data-exporting workflow returns instead of a raw *os.File — reports, dumps, archives, attachments, backups, media generation, document rendering, etc. Consumers (Telegram, S3, email, HTTP download, Discord, storage, backup workers) depend only on Resource and the standard io interfaces; they never know whether the bytes live in memory, on disk, or in a streaming pipe.

Contract
type ReadSeekCloser interface {
    io.Reader
    io.Seeker
    io.Closer
}

// All fields are unexported; access via getters / chainable setters.
type Resource struct{ /* name, size, contentType, content, ... */ }

Accessors:

res.Name()           string
res.Size()           int64
res.ContentType()    string
res.Content()        sysx.ReadSeekCloser
res.SpillThreshold() int64
res.TempPattern()    string
res.TempDir()        string
res.RemoveOnClose()  bool

Chainable setters (configure-then-load builder):

res.WithName(name)              *Resource
res.WithContentType(mime)       *Resource
res.WithSize(n)                 *Resource
res.WithContent(rsc)            *Resource
res.WithSpillThreshold(bytes)   *Resource
res.WithTempPattern(pattern)    *Resource
res.WithTempDir(dir)            *Resource
res.WithRemoveOnClose(bool)     *Resource

Loaders (each takes a single source):

res.FromBytes([]byte)                          *Resource
res.FromString(string)                         *Resource
res.FromFile(*os.File)                         (*Resource, error)
res.FromTempFile(func(io.Writer) error)        (*Resource, error)
res.FromReader(io.Reader)                      (*Resource, error)

Lifecycle / consumption:

res.Close()                  // release backing (idempotent, nil-safe)
res.Rewind()                 // seek to offset 0
res.CopyTo(io.Writer)        // stream payload into a sink
res.Drain()                  // consume and discard

Backings:

Type Storage Close()
*sysx.MemBlob byte slice no-op
*sysx.TempFile on-disk temporary file closes + unlinks (configurable)
(private) hybrid memory → spill file (FromReader) closes spill file when present

MIME helpers (in mime.go):

sysx.MimeFromName("dump.tar.gz")  // → "application/gzip"
sysx.MimeFromName("report.csv")   // → "text/csv; charset=utf-8"
// Constants: MimeOctetStream, MimeText, MimeCSV, MimeJSON, MimeXML,
//            MimeHTML, MimePDF, MimeZIP, MimeGZIP, MimeSQL
Producer examples
// 1) In-memory CSV
res := sysx.NewResource().
    WithName("hello.csv").
    FromString("a,b\n1,2\n")
defer res.Close()

// 2) Large export spilled to a temp file (auto-removed on Close)
res, err := sysx.NewResource().
    WithName("audit.csv").
    WithTempPattern("audit-*.csv").
    FromTempFile(func(w io.Writer) error {
        _, err := w.Write(payload)
        return err
    })

// 3) Streaming pg_dump | gzip — seekable for S3 multipart
cmd := exec.CommandContext(ctx, "pg_dump", "my_db")
stdout, _ := cmd.StdoutPipe()
_ = cmd.Start()
res, err := sysx.NewResource().
    WithName("dump.sql.gz").
    WithSpillThreshold(16 << 20).      // 16 MiB
    FromReader(stdout)

// 4) Adopt an existing on-disk file without copying
f, _ := os.Open("/tmp/snapshot.bin")
res, err := sysx.NewResource().
    WithRemoveOnClose(false).          // do not unlink the file on Close
    FromFile(f)
Consumer examples

Every consumer looks identical regardless of how the bytes were produced.

// HTTP download
func DownloadHandler(w http.ResponseWriter, r *http.Request, res *sysx.Resource) error {
    defer res.Close()
    w.Header().Set("Content-Disposition", "attachment; filename="+res.Name())
    w.Header().Set("Content-Type", res.ContentType())
    _, err := res.CopyTo(w)
    return err
}

// S3 upload
func UploadToS3(ctx context.Context, c S3Client, res *sysx.Resource) error {
    defer res.Close()
    _, err := c.PutObject(ctx, res.Name(), res.Content(), res.Size())
    return err
}

// Telegram upload
func SendToTelegram(ctx context.Context, bot TelegramBot, res *sysx.Resource) error {
    defer res.Close()
    return bot.SendDocument(ctx, res.Name(), res.Content())
}
Architectural principles
  1. Business code returns *sysx.Resource, never *os.File.
  2. Consumers depend on Resource and on the standard io interfaces.
  3. Implementations are interchangeable: memory, temp file, hybrid stream.
  4. Cleanup is explicit and lives behind Resource.Close (idempotent, nil-safe).
  5. New backings (object storage stream, encrypted pipe, database blob, …) plug in through WithContent without changes to existing consumers.

Resource Cookbook — Complete main Programs

This section walks through every backing (MemBlob, TempFile, the private hybrid spill buffer, and the high-level Resource) with self-contained main programs that compile as-is. Examples progress from the most basic shape to advanced production patterns.

All snippets assume the import block:

import (
     "bytes"
     "context"
     "encoding/csv"
     "fmt"
     "io"
     "log"
     "net/http"
     "os"
     "os/exec"
     "strings"
     "time"

     "github.com/sivaosorg/replify/pkg/sysx"
 )
MemBlob — In-Memory Backing

MemBlob wraps a []byte in a ReadSeekCloser. It is the cheapest backing, allocates nothing beyond the byte slice itself, and Close() is a no-op.

MB1. Basic: read a byte slice as a stream
package main

func main() {
    blob := sysx.NewMemBlob([]byte("hello, world\n"))
    defer blob.Close() // no-op, but kept for symmetry

    data, err := io.ReadAll(blob)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("len=%d bytes=%q\n", blob.Len(), data)
}
MB2. Seek, partial reads, and rewind
package main

func main() {
    blob := sysx.NewMemBlob([]byte("0123456789"))
    defer blob.Close()

    // Skip the first 4 bytes.
    if _, err := blob.Seek(4, io.SeekStart); err != nil {
        log.Fatal(err)
    }
    chunk := make([]byte, 3)
    if _, err := io.ReadFull(blob, chunk); err != nil {
        log.Fatal(err)
    }
    fmt.Printf("middle chunk: %q\n", chunk) // "456"

    // Rewind and read everything.
    if _, err := blob.Seek(0, io.SeekStart); err != nil {
        log.Fatal(err)
    }
    all, _ := io.ReadAll(blob)
    fmt.Printf("full payload: %q\n", all)
}
MB3. Inspect the underlying slice without copying
package main

func main() {
    payload := []byte("retain-not-copy")
    blob := sysx.NewMemBlob(payload)

    // Bytes() returns the same slice that was passed to NewMemBlob.
    // Callers must not mutate it for the lifetime of the MemBlob.
    same := blob.Bytes()
    fmt.Println("aliased:", &payload[0] == &same[0]) // true
    fmt.Println("len:    ", blob.Len())
}
MB4. Promote a MemBlob into a Resource
package main

func main() {
    res := sysx.NewResource().
        WithName("greeting.txt").
        FromBytes([]byte("hello\n")) // installs a *MemBlob internally
    defer res.Close()

    fmt.Println("name:        ", res.Name())
    fmt.Println("size:        ", res.Size())
    fmt.Println("content-type:", res.ContentType()) // text/plain; charset=utf-8

    n, err := res.CopyTo(os.Stdout)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("\ncopied %d bytes\n", n)
}
TempFile — On-Disk Backing

TempFile is the on-disk counterpart of MemBlob. It owns an *os.File created by os.CreateTemp, exposes idempotent Close, and can auto-remove the file when closed (default) or hand off ownership when disarmed.

TF1. Basic: write, rewind, read, auto-remove
package main

func main() {
    tf, err := sysx.NewTempFile()
    if err != nil {
        log.Fatal(err)
    }
    defer tf.Close() // closes + removes from disk

    if _, err := tf.Write([]byte("temporary payload")); err != nil {
        log.Fatal(err)
    }
    if _, err := tf.Seek(0, io.SeekStart); err != nil {
        log.Fatal(err)
    }

    out, _ := io.ReadAll(tf)
    fmt.Println("path:    ", tf.Path())
    fmt.Printf("payload: %q\n", out)
    fmt.Println("removeOnClose:", tf.RemoveOnClose()) // true
}
TF2. Custom pattern + custom directory
package main

func main() {
    // System temp dir, "audit-*.csv" pattern.
    tf1, err := sysx.NewTempFilename("audit-*.csv")
    if err != nil {
        log.Fatal(err)
    }
    defer tf1.Close()
    fmt.Println("tf1:", tf1.Path())

    // Explicit directory + pattern.
    tf2, err := sysx.NewTempFileAt(os.TempDir(), "session-*.bin")
    if err != nil {
        log.Fatal(err)
    }
    defer tf2.Close()
    fmt.Println("tf2:", tf2.Path())
}
TF3. Promote a TempFile into a permanent file

WithRemoveOnClose(false) disarms auto-removal so a downstream consumer can rename the file to its final destination without losing the bytes.

package main

func main() {
    tf, err := sysx.NewTempFilename("report-*.csv")
    if err != nil {
        log.Fatal(err)
    }

    if _, err := tf.Write([]byte("col1,col2\n1,2\n")); err != nil {
        _ = tf.Close()
        log.Fatal(err)
    }

    // Hand ownership to the filesystem; do NOT unlink on Close.
    tf.WithRemoveOnClose(false)
    if err := tf.Close(); err != nil {
        log.Fatal(err)
    }

    final := "/tmp/report-final.csv"
    if err := os.Rename(tf.Path(), final); err != nil {
        log.Fatal(err)
    }
    fmt.Println("kept at:", final)
}
TF4. Idempotent Close (safe to defer twice)
package main

func main() {
    tf, err := sysx.NewTempFile()
    if err != nil {
        log.Fatal(err)
    }
    defer tf.Close() // safety net

    _, _ = tf.Write([]byte("once"))

    // First Close performs the work.
    if err := tf.Close(); err != nil {
        log.Fatal(err)
    }
    fmt.Println("closed?", tf.Closed()) // true

    // Subsequent calls (including the deferred one) are no-ops.
}
TF5. Stat the underlying file before consumption
package main

func main() {
    tf, err := sysx.NewTempFilename("size-*.bin")
    if err != nil {
        log.Fatal(err)
    }
    defer tf.Close()

    _, _ = tf.Write(bytes.Repeat([]byte{0xAB}, 4096))

    info, err := tf.Stat()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("name=%s size=%d mode=%s\n", info.Name(), info.Size(), info.Mode())
}
spillBuffer — Hybrid Memory → Disk Backing (via Resource.FromReader)

The spill buffer is private; consumers reach it through Resource.FromReader. It keeps the first SpillThreshold bytes in memory and transparently overflows the rest to a temporary file, producing a ReadSeekCloser even when the source reader is one-shot (a process pipe, an HTTP body, …).

SB1. Small payload — stays in memory
package main

func main() {
    src := strings.NewReader("compact payload that fits in RAM")

    res, err := sysx.NewResource().
        WithName("note.txt").
        WithSpillThreshold(1 << 20). // 1 MiB ceiling
        FromReader(src)
    if err != nil {
        log.Fatal(err)
    }
    defer res.Close()

    fmt.Println("size:", res.Size())
    _, _ = res.CopyTo(os.Stdout)
}
SB2. Large payload — spills to disk transparently
package main

func main() {
    // 4 MiB of repeating data — well above the 64 KiB threshold below.
    src := bytes.NewReader(bytes.Repeat([]byte("X"), 4<<20))

    res, err := sysx.NewResource().
        WithName("dump.bin").
        WithSpillThreshold(64 << 10). // 64 KiB → triggers spill
        FromReader(src)
    if err != nil {
        log.Fatal(err)
    }
    defer res.Close() // unlinks the spill file (RemoveOnClose default = true)

    fmt.Println("size:", res.Size()) // 4 MiB
    n, _ := res.Drain()              // read & discard
    fmt.Println("drained:", n)
}
SB3. Custom temp dir and pattern for the spill file
package main

func main() {
    // Pretend pg_dump produces 50 MiB of SQL.
    src := bytes.NewReader(bytes.Repeat([]byte("INSERT ...;\n"), 4_000_000))

    res, err := sysx.NewResource().
        WithName("dump.sql").
        WithContentType(sysx.MimeSQL).
        WithSpillThreshold(8 << 20).      // 8 MiB
        WithTempDir("/var/tmp").          // dedicated spill volume
        WithTempPattern("pgdump-*.sql").  // human-readable name
        FromReader(src)
    if err != nil {
        log.Fatal(err)
    }
    defer res.Close()

    fmt.Println("name:", res.Name(), "size:", res.Size())
}
SB4. Streaming a subprocess into a seekable Resource

This is the canonical use case: a one-shot pipe becomes seekable so it can be uploaded by an S3 multipart client (which retries chunks).

package main

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()

    cmd := exec.CommandContext(ctx, "pg_dump", "my_db")
    stdout, err := cmd.StdoutPipe()
    if err != nil {
        log.Fatal(err)
    }
    if err := cmd.Start(); err != nil {
        log.Fatal(err)
    }

    res, err := sysx.NewResource().
        WithName("my_db.sql").
        WithContentType(sysx.MimeSQL).
        WithSpillThreshold(16 << 20). // 16 MiB
        FromReader(stdout)
    if err != nil {
        _ = cmd.Wait()
        log.Fatal(err)
    }
    defer res.Close()

    if err := cmd.Wait(); err != nil {
        log.Fatal(err)
    }

    // Re-readable: the spill backing is seekable.
    _ = res.Rewind()
    fmt.Println("dump bytes:", res.Size())
}
Resource — End-to-End Programs

Resource is the public envelope. Producers configure a backing through one of the From* loaders; consumers depend only on Resource plus standard io interfaces.

R1. Basic: in-memory CSV produced and copied to stdout
package main

func main() {
    res := sysx.NewResource().
        WithName("hello.csv").
        FromString("a,b\n1,2\n")
    defer res.Close()

    if _, err := res.CopyTo(os.Stdout); err != nil {
        log.Fatal(err)
    }
}
R2. Producer that writes through FromTempFile

FromTempFile is the right choice when the payload is large or expensive to re-compute and you want it on disk from the start. The producer never sees the underlying *os.File — only an io.Writer view.

package main

func main() {
    res, err := sysx.NewResource().
        WithName("users.csv").
        WithContentType(sysx.MimeCSV).
        WithTempPattern("users-*.csv").
        FromTempFile(func(w io.Writer) error {
            cw := csv.NewWriter(w)
            _ = cw.Write([]string{"id", "email"})
            _ = cw.Write([]string{"1", "alice@example.com"})
            _ = cw.Write([]string{"2", "bob@example.com"})
            cw.Flush()
            return cw.Error()
        })
    if err != nil {
        log.Fatal(err)
    }
    defer res.Close() // closes + unlinks the temp file

    fmt.Println("name:", res.Name(), "size:", res.Size())
    _, _ = res.CopyTo(os.Stdout)
}
R3. Adopt an existing *os.File with FromFile

FromFile is the only loader that accepts a raw *os.File. Use it when a file already exists on disk (snapshot, cached export, …) and you want to hand it to a consumer through the Resource contract.

package main

func main() {
    f, err := os.Open("/tmp/snapshot.bin")
    if err != nil {
        log.Fatal(err)
    }

    res, err := sysx.NewResource().
        WithRemoveOnClose(false). // keep the file after Close()
        FromFile(f)
    if err != nil {
        _ = f.Close()
        log.Fatal(err)
    }
    defer res.Close()

    fmt.Println("name:", res.Name(), "size:", res.Size(),
        "ct:", res.ContentType())
}
R4. Multi-consumer dispatch with Rewind

A single Resource can feed several consumers (S3, local archive, checksum) by rewinding between consumers.

package main

func main() {
    res := sysx.NewResource().
        WithName("payload.bin").
        FromBytes(bytes.Repeat([]byte{0x42}, 1024))
    defer res.Close()

    // Consumer 1: write to disk.
    out, err := os.Create("/tmp/payload.bin")
    if err != nil {
        log.Fatal(err)
    }
    if _, err := res.CopyTo(out); err != nil {
        _ = out.Close()
        log.Fatal(err)
    }
    _ = out.Close()

    // Consumer 2: compute size by draining (rewinding first).
    if err := res.Rewind(); err != nil {
        log.Fatal(err)
    }
    n, err := res.Drain()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("drained:", n)
}
R5. HTTP download handler — uniform consumer

The handler depends only on *sysx.Resource; it does not care whether the backing is memory, a temp file, or a spill buffer.

package main

func downloadHandler(produce func() (*sysx.Resource, error)) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        res, err := produce()
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
        defer res.Close()

        w.Header().Set("Content-Type", res.ContentType())
        w.Header().Set("Content-Disposition",
            fmt.Sprintf("attachment; filename=%q", res.Name()))
        if res.Size() >= 0 {
            w.Header().Set("Content-Length", fmt.Sprint(res.Size()))
        }
        if _, err := res.CopyTo(w); err != nil {
            log.Printf("copy failed: %v", err)
        }
    }
}

func main() {
    produce := func() (*sysx.Resource, error) {
        return sysx.NewResource().
            WithName("hello.txt").
            FromString("hello from sysx\n"), nil
    }
    http.HandleFunc("/download", downloadHandler(produce))
    log.Fatal(http.ListenAndServe(":8080", nil))
}
R6. Pick the right backing at runtime

The same producer chooses memory, on-disk, or hybrid streaming based on the size hint reported by the data source.

package main

// fetchPayload returns a Resource backed by the cheapest viable storage.
func fetchPayload(name string, sizeHint int64, src io.Reader) (*sysx.Resource, error) {
    base := sysx.NewResource().WithName(name)

    switch {
    case sizeHint >= 0 && sizeHint <= 64<<10:
        // Tiny — keep in memory.
        data, err := io.ReadAll(src)
        if err != nil {
            return nil, err
        }
        return base.FromBytes(data), nil

    case sizeHint >= 0 && sizeHint <= 8<<20:
        // Medium — buffer to a temp file in one shot.
        return base.WithTempPattern("payload-*.bin").
            FromTempFile(func(w io.Writer) error {
                _, err := io.Copy(w, src)
                return err
            })

    default:
        // Unknown or large — hybrid spill.
        return base.WithSpillThreshold(8 << 20).
            FromReader(src)
    }
}

func main() {
    res, err := fetchPayload("data.bin", -1, strings.NewReader("streamed payload"))
    if err != nil {
        log.Fatal(err)
    }
    defer res.Close()

    fmt.Println("size:", res.Size())
    _, _ = res.CopyTo(os.Stdout)
}
R7. Custom ReadSeekCloser via WithContent

Plug in any backing that satisfies sysx.ReadSeekCloser — encrypted pipes, object-storage streams, database blobs — without touching consumer code.

package main

// readSeekNopCloser wraps a *bytes.Reader and adds a no-op Close.
type readSeekNopCloser struct{ *bytes.Reader }

func (readSeekNopCloser) Close() error { return nil }

func main() {
    payload := []byte("custom-backed payload")
    rsc := readSeekNopCloser{bytes.NewReader(payload)}

    res := sysx.NewResource().
        WithName("custom.bin").
        WithContentType(sysx.MimeOctetStream).
        WithSize(int64(len(payload))).
        WithContent(rsc)
    defer res.Close()

    _, _ = res.CopyTo(os.Stdout)
    fmt.Println()
}
R8. MIME inference from the filename

MimeFromName powers the auto-detection performed by every From* loader when ContentType is left empty.

package main

func main() {
    cases := []string{
        "report.csv",
        "dump.tar.gz",
        "page.HTML",
        "schema.sql",
        "no-extension",
    }
    for _, name := range cases {
        fmt.Printf("%-15s → %s\n", name, sysx.MimeFromName(name))
    }
    // report.csv      → text/csv; charset=utf-8
    // dump.tar.gz     → application/gzip
    // page.HTML       → text/html; charset=utf-8
    // schema.sql      → application/sql
    // no-extension    → application/octet-stream
}
R9. Defensive consumer — nil-safe Close and Rewind

Every accessor and lifecycle method tolerates a nil receiver, so callers can defer Close even on the error path.

package main

func produce(fail bool) (*sysx.Resource, error) {
    if fail {
        return nil, fmt.Errorf("upstream failure")
    }
    return sysx.NewResource().FromString("ok"), nil
}

func main() {
    res, err := produce(true)
    defer res.Close() // safe even when res == nil

    if err != nil {
        fmt.Println("error path handled cleanly:", err)
        return
    }
    _, _ = res.CopyTo(os.Stdout)
}

Usage Examples

Basic OS / Architecture Check
fmt.Println("OS:      ", sysx.OSName())
fmt.Println("Arch:    ", sysx.Arch())
fmt.Println("Version: ", sysx.OSVersion())
fmt.Println("64-bit:  ", sysx.Is64Bit())
fmt.Println("ARM:     ", sysx.IsArm())
Environment Configuration
host  := sysx.Getenv("DB_HOST", "localhost")
port  := sysx.GetenvInt("DB_PORT", 5432)
debug := sysx.GetenvBool("DEBUG", false)
tags  := sysx.GetenvSlice("TAGS", ",")

// Panics at startup if DATABASE_URL is not configured
dsn := sysx.MustGetenv("DATABASE_URL")
System Information Snapshot
info := sysx.SystemInfo()
for k, v := range info {
    fmt.Printf("%-12s = %s\n", k, v)
}
// os           = linux
// arch         = amd64
// hostname     = myserver
// pid          = 12345
// go_version   = go1.24.0
// executable   = /usr/local/bin/myapp
// num_cpu      = 8

Command Execution Utilities

Simple execution
// Fire and forget
if err := sysx.ExecCommand("go", "generate", "./..."); err != nil {
    log.Fatal(err)
}

// Capture combined output
out, err := sysx.ExecOutput("git", "log", "--oneline", "-5")
fmt.Print(out)

// Capture stdout as lines
lines, err := sysx.ExecOutputLines("git", "branch", "-a")
for _, l := range lines {
    fmt.Println(l)
}
Advanced Command Execution Examples
Builder with full configuration
res := sysx.NewCommand("bash").
    WithArgs("-c", "echo $APP_ENV && ls /tmp").
    WithTimeout(10 * time.Second).
    WithEnv("APP_ENV=production").
    WithDir("/tmp").
    Execute()

if !res.IsSuccess() {
    log.Printf("command failed (exit %d, %.2fs):\nstdout: %s\nstderr: %s",
        res.ExitCode, res.Duration.Seconds(), res.Stdout, res.Stderr)
}
Stream output in real time
// Print build output line-by-line as it happens
err := sysx.ExecStreaming(os.Stdout, os.Stderr, "go", "build", "-v", "./...")
Async execution
cmd, err := sysx.ExecAsync("server", "--port", "8080")
if err != nil {
    log.Fatal(err)
}
// ... do other work while server runs ...
if err := cmd.Wait(); err != nil {
    log.Println("server exited:", err)
}
Shell-style pipeline
out, err := sysx.ExecPipeline(
    []string{"cat", "/var/log/syslog"},
    []string{"grep", "ERROR"},
    []string{"wc", "-l"},
)
fmt.Println("error lines:", strings.TrimSpace(out))
Context-controlled execution
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := sysx.ExecCommandContext(ctx, "go", "test", "-race", "./..."); err != nil {
    log.Fatal(err)
}
Structured result inspection
res := sysx.RunCommand("golangci-lint", "run", "./...")
fmt.Printf("exit=%d duration=%v\n", res.ExitCode, res.Duration.Round(time.Millisecond))
if !res.IsSuccess() {
    fmt.Println("--- stdout ---")
    fmt.Print(res.Stdout)
    fmt.Println("--- stderr ---")
    fmt.Print(res.Stderr)
}

File I/O Utilities

Reading files
// Read entire file
data, err := sysx.ReadFile("/etc/hosts")

// Read as string
content, err := sysx.ReadFileString("/etc/hostname")
fmt.Println(strings.TrimSpace(content))

// Read line-by-line into slice
lines, err := sysx.ReadLines("/var/log/app.log")
fmt.Printf("%d log entries\n", len(lines))
Streaming large files
// Memory-efficient: one line in memory at a time
err := sysx.StreamLines("/var/log/big.log", func(line string) error {
    if strings.Contains(line, "CRITICAL") {
        alertOnCall(line)
    }
    return nil
})

Early termination example:

var found string
err := sysx.StreamLines("/etc/passwd", func(line string) error {
    if strings.HasPrefix(line, "root:") {
        found = line
        return errors.New("stop") // stop processing immediately
    }
    return nil
})
Writing files
// Create / truncate
sysx.WriteFile("/tmp/output.bin", data)
sysx.WriteFileString("/tmp/config.txt", configContent)

// Append
sysx.AppendFile("/var/log/app.log", []byte("new entry\n"))
sysx.AppendString("/var/log/app.log", "another entry\n")

// Write line slice (each element newline-terminated, buffered writer)
sysx.WriteLines("/tmp/list.txt", []string{"alpha", "beta", "gamma"})

Concurrency-Safe File Operations

Atomic writes — no partial reads

AtomicWriteFile uses the temp-file + rename pattern. On POSIX systems os.Rename is atomic within the same filesystem, so readers will never observe a partial write.

// Safe for readers running concurrently:
if err := sysx.AtomicWriteFile("/etc/app/config.json", newJSON); err != nil {
    log.Fatal(err)
}
Per-path in-process locking

WriteFileLocked serialize concurrent writes to the same path using a package-level sync.Map of mutexes. No goroutine observes a half-written file within the same process.

// Safe to call from multiple goroutines targeting the same path:
go sysx.WriteFileLocked("/tmp/shared.dat", payload1)
go sysx.WriteFileLocked("/tmp/shared.dat", payload2)
SafeFileWriter — reusable concurrent writer

Share a single SafeFileWriter instance across goroutines for safe append operations. The internal mutex serialize every Write, WriteString, and Overwrite call.

w := sysx.NewSafeFileWriter("/var/log/app.log").WithPerm(0o600)

// Fan-out writes from multiple goroutines — no external lock required:
for i := 0; i < 100; i++ {
    go func(n int) {
        w.WriteString(fmt.Sprintf("goroutine %d\n", n))
    }(i)
}

Use Overwrite for atomic replacement through the same writer:

w.Overwrite(updatedConfig) // temp-file + rename under the mutex

Performance Considerations

Operation Implementation detail
ReadLines / StreamLines bufio.Scanner — one line in memory, large files handled efficiently
WriteLines bufio.Writer — batches small writes into a single syscall
commandBuffer (internal) strings.Builder — avoids bytes.Buffer allocations for command output
AtomicWriteFile single os.Rename syscall; no extra read pass
WriteFileLocked sync.Map + per-path sync.Mutex — O(1) contention lookup
SafeFileWriter single mutex per instance — lowest overhead for repeated appends to one file

Guidelines:

  • For large files, always prefer StreamLines over ReadLines to avoid loading the whole file into memory.
  • Re-use a SafeFileWriter instance across goroutines rather than calling AppendFile concurrently (which uses no lock).
  • Use AtomicWriteFile when readers and writers run concurrently; it prevents any reader from seeing partial data.

Best Practices

  1. Prefer Must* only at startup — use error-returning variants in library code; reserve Must* for main() or init() where a missing resource is fatal.
  2. Check ProcessExists before signallingos.FindProcess on Unix always succeeds; pair it with ProcessExists to avoid signalling phantom PIDs.
  3. Always set a timeout in long-running services — use WithTimeout or ExecCommandWithTimeout when calling external programs.
  4. ExecOutput* merges stdout+stderr — use the Command builder with WithStdout/WithStderr to separate them when needed.
  5. EnvMap is a snapshot — the returned map is not updated when the environment changes; call again if you need fresh values.
  6. Prefer AtomicWriteFile over WriteFile for critical dataWriteFile uses os.WriteFile which is not atomic on all kernels.
  7. WriteFileLocked is in-process only — for cross-process safety combine it with AtomicWriteFile or use a platform lock.

Platform Caveats

Feature Linux macOS Windows
UID() / GID() numeric numeric always -1
PPID() supported supported always 0
ProcessExists signal(0) signal(0) FindProcess (unreliable for dead PIDs)
KillProcess SIGTERM SIGTERM limited
KillProcessForcefully SIGKILL SIGKILL limited
IsExecutable / IsReadable mode bits mode bits approximation
IsWritable open test open test open test
OSVersion /etc/os-release sw_vers GOOS/GOARCH string
IsPrivileged UID==0 UID==0 always false
AtomicWriteFile rename atomic (same fs) atomic (same fs) not guaranteed atomic
ExecStreaming / ExecPipeline full support full support partial support

When to Use sysx vs stdlib

Task Prefer
Quick OS check sysx.IsLinux() vs runtime.GOOS == "linux" — both work; sysx adds readability
Env var with fallback sysx.Getenv — stdlib requires a conditional around os.LookupEnv
Typed env vars sysx.GetenvInt / sysx.GetenvBool — no stdlib equivalent
Run command and capture output sysx.ExecOutput — saves wiring cmd.Stdout, cmd.Stderr
Command with timeout sysx.ExecCommandWithTimeout — saves context.WithTimeout boilerplate
Stream command output sysx.ExecStreaming — pass any io.Writer
Check if a file exists sysx.FileExists — stdlib os.Stat + error check
Read file as lines sysx.ReadLines / sysx.StreamLines — stdlib requires bufio.Scanner setup
Write file atomically sysx.AtomicWriteFile — stdlib has no atomic helper
Concurrent file writes sysx.SafeFileWriter / WriteFileLocked — stdlib requires manual mutex
Check if a process is alive sysx.ProcessExists — stdlib requires syscall.Kill(pid, 0) and type assertion
Complex I/O piping os/exec directly — sysx.ExecPipeline covers linear chains only
Watching the file system fsnotify or stdlib — outside sysx scope

Real-World Integration Examples

Health-Check Endpoint
func healthHandler(w http.ResponseWriter, r *http.Request) {
    info := sysx.SystemInfo()
    json.NewEncoder(w).Encode(map[string]any{
        "status":     "ok",
        "hostname":   info["hostname"],
        "pid":        info["pid"],
        "go_version": info["go_version"],
        "num_cpu":    info["num_cpu"],
    })
}
Startup Configuration Validation
func mustLoadConfig() Config {
    return Config{
        DSN:     sysx.MustGetenv("DATABASE_URL"),
        Port:    sysx.GetenvInt("PORT", 8080),
        Debug:   sysx.GetenvBool("DEBUG", false),
        Origins: sysx.GetenvSlice("CORS_ORIGINS", ","),
    }
}
Conditional Platform Logic
func configureLogging() {
    switch {
    case sysx.IsLinux():
        // Use journald integration
    case sysx.IsDarwin():
        // Use os_log
    default:
        // Fallback to stderr
    }
}
CI Pipeline Step Runner
steps := [][]string{
    {"go", "vet", "./..."},
    {"go", "build", "./..."},
    {"go", "test", "-race", "-cover", "./..."},
}
for _, step := range steps {
    res := sysx.RunCommand(step[0], step[1:]...)
    fmt.Printf("$ %s => exit=%d (%.2fs)\n",
        strings.Join(step, " "), res.ExitCode, res.Duration.Seconds())
    if !res.IsSuccess() {
        fmt.Fprintln(os.Stderr, res.Combined())
        os.Exit(1)
    }
}
Concurrent Log Aggregator
w := sysx.NewSafeFileWriter("/var/log/aggregated.log")
var wg sync.WaitGroup
for _, source := range logSources {
    wg.Add(1)
    go func(src string) {
        defer wg.Done()
        sysx.StreamLines(src, func(line string) error {
            return w.WriteString(line + "\n")
        })
    }(source)
}
wg.Wait()
Atomic Config Reload
func reloadConfig(newCfg []byte) error {
    // Validate before replacing
    if err := validateConfig(newCfg); err != nil {
        return err
    }
    // Replace atomically so readers always see a complete file
    return sysx.AtomicWriteFile("/etc/app/config.json", newCfg)
}

Network Utilities

The net.go file provides helpers for common network operations used in production backend services. All functions use only the Go standard library and are safe for concurrent use.

IP and Address Utilities
// Classify IP addresses
sysx.IsIPv4("192.168.1.1")   // true
sysx.IsIPv6("::1")           // true
sysx.IsLocalIP("10.0.0.1")   // true (RFC 1918 private range)
sysx.IsLocalIP("8.8.8.8")    // false (public)

// Get machine addresses
localIP, _ := sysx.GetLocalIP()           // first non-loopback IPv4
publicIP, _ := sysx.GetPublicIP()         // via api.ipify.org
allIPs, _ := sysx.GetInterfaceIPs()       // all IPs from all interfaces
Port Probing
// Check whether a remote port is reachable (production service health check)
if !sysx.IsPortOpen("postgres.internal", 5432) {
    log.Fatal("database unreachable")
}

// Check whether a local port is free before binding
if !sysx.IsPortAvailable(8080) {
    log.Fatal("port 8080 already in use")
}
URL and Host Validation
// Validate URLs before making requests
if !sysx.IsValidURL(webhookURL) {
    return fmt.Errorf("invalid webhook URL: %q", webhookURL)
}

// Parse host:port strings from configuration
host, port, err := sysx.ParseHostPort(config.DBAddr)
// host == "db.internal", port == 5432

// Verify a host is DNS-resolvable before connecting
if !sysx.IsValidHost(host) {
    return fmt.Errorf("cannot resolve host: %q", host)
}
Connectivity Checks
// Quick reachability probe (TCP port 80)
if err := sysx.PingHost("api.internal"); err != nil {
    log.Printf("API host unreachable: %v", err)
}

// Explicit TCP connection check with custom timeout
err := sysx.CheckTCPConn("redis.cache", 6379, 2*time.Second)
if err != nil {
    log.Printf("Redis unavailable: %v", err)
}
Real-World: Startup Dependency Check
func checkDependencies() error {
    deps := []struct{ name, host string; port int }{
        {"PostgreSQL", "db.internal", 5432},
        {"Redis",      "cache.internal", 6379},
        {"Kafka",      "broker.internal", 9092},
    }
    for _, dep := range deps {
        if err := sysx.CheckTCPConn(dep.host, dep.port, 5*time.Second); err != nil {
            return fmt.Errorf("%s is not reachable: %w", dep.name, err)
        }
        log.Printf("✓ %s (%s:%d) reachable", dep.name, dep.host, dep.port)
    }
    return nil
}
Real-World: Dynamic Port Selection
func findFreePort(start, end int) (int, error) {
    for port := start; port <= end; port++ {
        if sysx.IsPortAvailable(port) {
            return port, nil
        }
    }
    return 0, fmt.Errorf("no free port in range %d-%d", start, end)
}
Platform Notes for Network Utilities
Feature Linux macOS Windows
GetLocalIP supported supported supported
GetPublicIP needs internet needs internet needs internet
IsPortAvailable TCP listen TCP listen TCP listen
PingHost TCP port 80 TCP port 80 TCP port 80
CheckTCPConn full support full support full support

Note: GetPublicIP requires outbound internet access. It will fail in air-gapped environments. Always handle its error gracefully in production code.


Documentation

Overview

Package sysx provides a lightweight, production-grade system utilities toolkit for interacting with the underlying operating system, process environment, runtime, network, and file system from within Go programs.

All functions are designed to be safe for concurrent use and impose no external dependencies beyond the Go standard library.

Package Architecture

Struct definitions and package-level globals live in type.go. Each file focuses on a single concern:

  • type.go — struct definitions (Command, CommandResult, SafeFileWriter), getters
  • command.go — command builder, Execute/Run/Output, and convenience functions
  • file.go — file existence/permission checks, read/write/atomic utilities
  • dir.go — directory creation, removal, and listing utilities
  • path.go — path manipulation helpers (base name, dir name, extension, join)
  • io.go — stream-oriented I/O helpers (CountLines, Head, CopyFile)
  • net.go — IP classification, port probing, address helpers, connectivity
  • env.go — environment variable helpers
  • os.go — OS detection and version
  • process.go — process existence, signalling, lookup
  • runtime.go — hostname, PID, UID, CPU count, memory stats
  • utilities.go — internal helpers and UserInfo()
  • entry.go — SystemInfo(), IsPrivileged()
  • resource.go — Resource builder (NewResource + With*/From*) and lifecycle
  • memblob.go — MemBlob: in-memory ReadSeekCloser backing
  • tempfile.go — TempFile: on-disk temporary file backing with auto-cleanup
  • spillbuf.go — private hybrid memory→disk buffer for streaming producers
  • mime.go — MimeFromName helper and ErrNilResource

OS Detection

sysx.IsLinux()    // true when running on Linux
sysx.IsDarwin()   // true when running on macOS/Darwin
sysx.IsWindows()  // true when running on Windows
sysx.OSName()     // returns runtime.GOOS (e.g. "linux", "darwin", "windows")
sysx.Arch()       // returns runtime.GOARCH (e.g. "amd64", "arm64")
sysx.OSVersion()  // best-effort human-readable OS version string

Runtime Information

sysx.Hostname()       // os.Hostname(), returns (string, error)
sysx.PID()            // current process identifier
sysx.NumCPU()         // number of logical CPUs
sysx.NumGoroutine()   // number of active goroutines
sysx.GoVersion()      // Go runtime version string (e.g. "go1.24.0")
sysx.MemStats()       // snapshot of runtime memory statistics

Environment Utilities

sysx.Getenv(key, fallback)      // env var with fallback
sysx.MustGetenv(key)            // panics if env var is absent or empty
sysx.Hasenv(key)                // true when var exists and is non-empty
sysx.GetenvInt(key, fallback)   // env var parsed as int
sysx.GetenvBool(key, fallback)  // env var parsed as bool
sysx.GetenvSlice(key, sep)      // env var split into a slice
sysx.EnvMap()                   // all env vars as map[string]string

Process Utilities

sysx.ProcessExists(pid)         // true when process with pid is running
sysx.KillProcess(pid)           // send SIGTERM to process
sysx.KillProcessForcefully(pid) // send SIGKILL to process
sysx.CurrentProcessName()       // base name of the current executable
sysx.FindProcessByPID(pid)      // *os.Process for the given pid

Command Execution

Builder API — configure once, execute cleanly:

sysx.NewCommand("bash").
	WithArgs("-c", "echo hello").
	WithTimeout(5 * time.Second).
	WithEnv("APP_ENV=prod").
	WithDir("/tmp").
	Execute()       // returns *CommandResult

All CommandResult fields are unexported; read them via accessors:

res.Stdout()    string
res.Stderr()    string
res.ExitCode()  int
res.Duration()  time.Duration
res.Err()       error
res.IsSuccess()   bool
res.Combined()  string

Convenience functions:

sysx.RunCommand(name, args...)                             // structured *CommandResult
sysx.ExecCommand(name, args...)                            // run, discard output
sysx.ExecCommandContext(ctx, name, args...)                // run under context
sysx.ExecOutput(name, args...)                             // run, capture combined output
sysx.ExecOutputLines(name, args...)                        // run, capture stdout as []string
sysx.ExecStreaming(stdout, stderr, name, args...)           // stream output in real time
sysx.ExecAsync(name, args...)                              // start without waiting
sysx.ExecPipeline([]string{...}, []string{...})            // shell-style pipe chain
sysx.ExecCommandWithTimeout(timeout, name, args...)        // run with deadline
sysx.ExecOutputWithTimeout(timeout, name, args...)         // run with deadline, capture
sysx.ExecCommandInDir(dir, name, args...)                  // run in directory
sysx.ExecOutputInDir(dir, name, args...)                   // run in directory, capture

File System Utilities

Existence and type checks:

sysx.FileExists(path)    // true when a file system entry exists at path
sysx.DirExists(path)     // true when a directory exists at path
sysx.IsFile(path)        // true when path is a regular file
sysx.IsDir(path)         // true when path is a directory
sysx.IsSymlink(path)     // true when path is a symbolic link
sysx.IsExecutable(path)  // true when file is executable by owner
sysx.IsReadable(path)    // true when file is readable by owner
sysx.IsWritable(path)    // true when file can be opened for writing
sysx.FileSize(path)      // size of file in bytes
sysx.FileMode(path)      // permission bits of the file
sysx.FileModTime(path)   // last modification time of the file
sysx.HomeDir()           // user's home directory
sysx.WorkingDir()        // current working directory

File reading:

sysx.ReadFile(path)                     // []byte contents
sysx.ReadFileString(path)               // string contents
sysx.ReadLines(path)                    // []string, one element per line
sysx.StreamLines(path, handler)         // line-by-line callback, memory-efficient

File writing:

sysx.WriteFile(path, data)              // create/truncate, write bytes
sysx.WriteFileString(path, content)     // create/truncate, write string
sysx.AppendFile(path, data)             // append bytes
sysx.AppendString(path, content)        // append string
sysx.WriteLines(path, lines)            // write slice as newline-terminated lines

Concurrency-safe and atomic writes:

sysx.AtomicWriteFile(path, data)        // temp-file + rename, prevents partial reads
sysx.WriteFileLocked(path, data)        // per-path in-process mutex, serialize writers
sysx.NewSafeFileWriter(path)            // reusable mutex-protected writer for one path

Directory Utilities

sysx.CreateDir(path)               // create directory and all parents (MkdirAll, 0755)
sysx.RemoveDir(path)               // remove directory and all contents (RemoveAll)
sysx.ListDir(path)                 // []string of all entry names in a directory
sysx.ListDirFiles(path)            // []string of regular file names only
sysx.ListDirDirs(path)             // []string of subdirectory names only

Path Helpers

sysx.BaseName(path)                // last element of path (filepath.Base)
sysx.DirName(path)                 // directory component of path (filepath.Dir)
sysx.Ext(path)                     // file extension including leading dot (filepath.Ext)
sysx.AbsPath(path)                 // absolute representation of path (filepath.Abs)
sysx.JoinPath(elem...)             // join path elements (filepath.Join)
sysx.CleanPath(path)               // lexically clean path (filepath.Clean)
sysx.SplitPath(path)               // split into (dir, file) components

Stream I/O Utilities

sysx.CountLines(path)              // count newline-delimited lines in a file
sysx.Head(path, n)                 // first n lines of a file as []string
sysx.CopyFile(src, dst)            // copy a file from src to dst
sysx.TruncateFile(path, size)      // truncate or extend a file to size bytes

Network Utilities

IP classification:

sysx.IsIPv4(ip)          // true for valid IPv4 dotted-decimal strings
sysx.IsIPv6(ip)          // true for valid IPv6 strings (non-IPv4)
sysx.IsLocalIP(ip)       // true for loopback, link-local, and RFC 1918 addresses

Port probing:

sysx.IsPortOpen(host, port)      // true when TCP connect to host:port succeeds
sysx.IsPortAvailable(port)       // true when the port can be bound locally

Network address helpers:

sysx.GetLocalIP()          // first non-loopback IPv4 from local interfaces
sysx.GetPublicIP()         // public IP via https://api.ipify.org (requires internet)
sysx.GetInterfaceIPs()     // all IPv4+IPv6 addresses across all interfaces

URL and host helpers:

sysx.IsValidHost(host)       // true when host resolves via DNS or is a valid IP
sysx.ParseHostPort(addr)     // split "host:port" into (host, port, error)
sysx.IsValidURL(rawURL)      // true when rawURL has a valid scheme and host

Connectivity:

sysx.PingHost(host)                              // TCP probe to port 80, 5s timeout
sysx.CheckTCPConn(host, port, timeout)     // TCP connect with explicit timeout

Resource: storage-agnostic exports

Resource is the envelope every data-exporting workflow (reports, dumps, archives, attachments, backups, media generation, document rendering) returns instead of a raw *os.File. Consumers — Telegram bots, S3 uploader, email services, HTTP download handlers, Discord bots, generic storage services, backup workers — depend exclusively on Resource and on the standard io interfaces.

Builder API:

res := sysx.NewResource().
    WithName("user-report.csv").
    WithContentType(sysx.MimeCSV).      // optional; auto-derived from name
    FromBytes(payload)                   // or FromString / FromReader / FromTempFile / FromFile
defer res.Close()

Configuration setters (all chainable):

res.WithName(string)                 // suggested filename
res.WithContentType(string)          // IANA media type
res.WithSize(int64)                  // explicit size for custom backings
res.WithContent(ReadSeekCloser)      // attach a custom backing
res.WithSpillThreshold(int64)        // FromReader memory ceiling
res.WithTempPattern(string)          // os.CreateTemp pattern
res.WithTempDir(string)              // parent dir for temp files
res.WithRemoveOnClose(bool)          // arm/disarm temp-file cleanup

Loaders (single-input "From*" methods):

res.FromBytes([]byte)                          *Resource
res.FromString(string)                         *Resource
res.FromFile(*os.File)                         (*Resource, error)
res.FromTempFile(func(io.Writer) error)        (*Resource, error)
res.FromReader(io.Reader)                      (*Resource, error)

Lifecycle and consumption:

res.Close()                          // release backing (idempotent)
res.Rewind()                         // seek to offset 0
res.CopyTo(io.Writer)                // stream payload into a sink
res.Drain()                          // consume and discard

Built-in backings:

*sysx.MemBlob   — bytes-only, no I/O; Close is a no-op
*sysx.TempFile  — on-disk temporary file with auto-removal on Close
(private)       — hybrid memory→spill buffer used by FromReader

MIME constants and helpers:

sysx.MimeCSV, MimeJSON, MimePDF, MimeGZIP, MimeOctetStream, …
sysx.MimeFromName("dump.tar.gz")     // returns MimeGZIP

Concurrency and Safety Notes

All exported functions in this package are safe for concurrent use. WriteFileLocked and SafeFileWriter provide in-process serialization via sync.Mutex. For cross-process atomicity, use AtomicWriteFile which relies on the atomic rename(2) syscall on POSIX systems.

Cross-Platform Notes

Path separator: use JoinPath and CleanPath instead of manual string concatenation to ensure correct behavior on all platforms.

File permissions: mode bits set by CreateDir (0755) and write functions (0644) are subject to the process umask on Unix. On Windows, mode bits are an approximation; all files typically report as executable.

Symlinks:

IsFile, IsDir, FileSize, FileMode, and FileModTime

follow symbolic links. IsSymlink uses os.Lstat and does not follow links.

Index

Constants

View Source
const (
	MimeOctetStream = "application/octet-stream"
	MimeText        = "text/plain; charset=utf-8"
	MimeCSV         = "text/csv; charset=utf-8"
	MimeJSON        = "application/json"
	MimeXML         = "application/xml"
	MimeHTML        = "text/html; charset=utf-8"
	MimePDF         = "application/pdf"
	MimeZIP         = "application/zip"
	MimeGZIP        = "application/gzip"
	MimeSQL         = "application/sql"
)

IANA media-type constants used by Resource producers and consumers. They are declared as untyped string constants so callers may pass them in any string context without conversion.

View Source
const DefaultSpillThreshold int64 = 8 << 20 // 8 MiB

DefaultSpillThreshold is the default in-memory ceiling used by Resource builders before spilling the remainder of a stream onto a temporary file. It balances avoidance of disk I/O for typical exports against protection from runaway memory usage when streaming arbitrary producers.

Variables

View Source
var (
	// ErrNilResource is returned by Resource helpers when the receiver, its
	// Content, or a required argument is nil.
	ErrNilResource = errors.New("sysx: nil resource or nil content")
)

Functions

func AbsPath

func AbsPath(path string) (string, error)

AbsPath returns an absolute representation of path. If path is not already absolute, it is joined with the current working directory to make it absolute. The result is cleaned via filepath.Clean.

It wraps filepath.Abs and is safe for concurrent use.

Parameters:

  • `path`: the file system path to convert.

Returns:

(string, error): the absolute path and nil on success, or an empty string
and a non-nil error if the current working directory cannot be determined.

Example:

abs, err := sysx.AbsPath("relative/dir")
if err != nil {
    log.Fatal(err)
}
fmt.Println(abs) // e.g. "/home/user/relative/dir"

func AppendBytes added in v0.1.11

func AppendBytes(path string, data []byte) error

AppendBytes appends data to the file at path, creating it with permission 0644 if it does not exist.

Parameters:

  • `path`: the destination file path.
  • `data`: the bytes to append.

Returns:

An error if the file could not be opened or written; nil on success.

Example:

if err := sysx.AppendBytes("/var/log/app.log", []byte("entry\n")); err != nil {
    log.Fatal(err)
}

func AppendOrCopyFrom added in v0.1.24

func AppendOrCopyFrom(path string, src io.Reader, sep []byte) error

AppendOrCopyFrom writes data from src to path using the same append-or-create strategy as AppendOrWriteBytes, but accepts an io.Reader so arbitrarily large payloads are never fully buffered in memory.

  • If the file does not exist or is empty, src is written atomically via the temp-file-and-rename pattern so readers never observe a partial write.
  • If the file already exists and contains data, sep followed by the bytes from src is appended. The OS-level O_APPEND flag ensures concurrent appenders from separate processes do not interleave partial writes.

Parent directories are created automatically (idempotent).

Parameters:

  • `path`: the destination file path.
  • `src`: an io.Reader providing the payload; consumed exactly once.
  • `sep`: separator prepended to src only when appending to a non-empty file; pass nil or an empty slice to append without a separator.

Returns:

An error if any step fails; nil on success.

Example:

// Stream-append JSON objects separated by a newline (NDJSON / JSON-Lines):
if err := sysx.AppendOrCopyFrom("/var/log/app/bodies.jsonl", src, []byte("\n")); err != nil {
    log.Fatal(err)
}

func AppendOrWriteBytes added in v0.1.24

func AppendOrWriteBytes(path string, data, sep []byte) error

AppendOrWriteBytes writes data to path using the following strategy:

  • If the file does not exist or is empty, data is written atomically via the temp-file-and-rename pattern (same as AtomicWriteBytes).
  • If the file already exists and contains data, sep followed by data is appended, so the caller can choose a suitable record separator (e.g. "\n" for JSON-Lines / NDJSON, "\n---\n" for human-readable multi-entry logs).

Parent directories are created automatically (idempotent).

Parameters:

  • `path`: the destination file path.
  • `data`: the bytes to write or append.
  • `sep`: separator prepended to data only when appending to a non-empty file; pass nil or an empty slice to append without a separator.

Returns:

An error if any step fails; nil on success.

Example:

// Append JSON objects separated by a newline (NDJSON / JSON-Lines):
if err := sysx.AppendOrWriteBytes("/var/log/app/responses.jsonl", entry, []byte("\n")); err != nil {
    log.Fatal(err)
}

func AppendString

func AppendString(path string, content string) error

AppendString appends content to the file at path, creating it with permission 0644 if it does not exist.

Parameters:

  • `path`: the destination file path.
  • `content`: the string to append.

Returns:

An error if the file could not be opened or written; nil on success.

Example:

if err := sysx.AppendString("/var/log/app.log", "entry\n"); err != nil {
    log.Fatal(err)
}

func Arch

func Arch() string

Arch returns the CPU architecture as reported by the Go runtime.

The value is identical to runtime.GOARCH and is one of the architecture strings defined by the Go toolchain (e.g. "amd64", "arm64", "386", "arm", "riscv64").

Returns:

A non-empty string identifying the current CPU architecture.

Example:

fmt.Println(sysx.Arch()) // "amd64"

func AtomicOpenFile added in v0.1.11

func AtomicOpenFile(path string, flags FileOpenFlags, perm os.FileMode) (*os.File, error)

AtomicOpenFile opens a file with strong consistency and isolation guarantees. It uses a combination of in-process mutexes (via getFileMutex) and cross-process advisory locking (flock/LockFileEx) to ensure that the file handle is protected from concurrent access.

The lock is automatically released when the returned f.Close() is called.

Parameters:

  • `path`: the file system path to open.
  • `flags`: the type-safe FileOpenFlags combination.
  • `perm`: the os.FileMode to apply if the file is created.

Returns:

(*os.File, error): a handle to the isolated file and nil on success.

Example:

f, err := sysx.AtomicOpenFile("config.json", sysx.RW, 0644)
if err != nil {
    log.Fatal(err)
}
defer f.Close() // releases the lock

func AtomicWriteBytes added in v0.1.11

func AtomicWriteBytes(path string, data []byte) error

AtomicWriteBytes writes data to path atomically using the temporary-file-and-rename pattern: data is first flushed to a temporary file in the same directory as path, then renamed to path.

Atomicity guarantees by platform:

  • Linux / macOS (native FS): rename(2) atomically replaces the destination — readers never see a partial write.
  • Windows: MoveFileEx(MOVEFILE_REPLACE_EXISTING) provides the same guarantee on the same volume and works even when the destination already exists.
  • exFAT / FAT32 / SMB mounts: rename returns EEXIST when the destination already exists; a remove-then-rename fallback is used. This sacrifices strict atomicity on those filesystems, but prevents the EEXIST failure.

Security note (CWE-732): os.CreateTemp creates the staging file with mode 0600 (owner-only). After the rename the final file is explicitly chmod'd to 0644. Callers writing sensitive data should call os.Chmod afterward.

Parameters:

  • `path`: the destination file path; parent directories are created automatically.
  • `data`: the bytes to write.

Returns:

An error if any step fails; nil on success.

Example:

if err := sysx.AtomicWriteBytes("/etc/app/config.json", jsonData); err != nil {
    log.Fatal(err)
}

func BaseName

func BaseName(path string) string

BaseName returns the last element of path. Trailing path separators are removed before extracting the final element. If path is empty, BaseName returns ".".

It wraps filepath.Base and is safe for concurrent use.

Parameters:

  • `path`: the file system path.

Returns:

A string containing the base name component of path.

Example:

sysx.BaseName("/etc/hosts")   // "hosts"
sysx.BaseName("/usr/bin/")    // "bin"
sysx.BaseName("")             // "."

func CheckTCPConn

func CheckTCPConn(host string, port int, timeout time.Duration) error

CheckTCPConn verifies that a TCP connection can be established to host:port within the specified timeout duration.

The connection is closed immediately after being established; no data is exchanged. An error is returned on connection failure, timeout, or when an invalid port is provided.

Parameters:

  • `host`: the host name or IP address to connect to.
  • `port`: the TCP port number (1-65535).
  • `timeout`: the maximum duration to wait for the connection.

Returns:

An error if the connection could not be established within timeout; nil on success.

Example:

err := sysx.CheckTCPConn("db.internal", 5432, 3*time.Second)
if err != nil {
    log.Printf("database unreachable: %v", err)
}

func CleanPath

func CleanPath(path string) string

CleanPath returns the shortest path name equivalent to path by purely lexical processing. It applies the same rules as filepath.Clean: it eliminates redundant separators, "." and ".." elements.

It wraps filepath.Clean and is safe for concurrent use.

Parameters:

  • `path`: the file system path to clean.

Returns:

A string containing the cleaned path. If the argument is empty, CleanPath
returns ".".

Example:

sysx.CleanPath("/usr//local/./bin/../lib") // "/usr/local/lib"
sysx.CleanPath("")                          // "."

func ClearDir added in v0.1.11

func ClearDir(path string) error

ClearDir removes all files and subdirectories from the directory at path, but leaves the directory itself intact.

If path does not exist, an error is returned.

Parameters:

  • `path`: the directory path to clear.

Returns:

An error if the directory could not be read or its contents removed; nil
on success.

Example:

if err := sysx.ClearDir("/tmp/cache"); err != nil {
    log.Fatal(err)
}

func CopyDir added in v0.1.11

func CopyDir(src, dst string) error

CopyDir recursively copies the directory tree from src to dst.

The destination directory is created with the same permissions as src. Existing files in the destination are overwritten. Symbolic links within the source are replicated at the destination (not followed).

Parameters:

  • `src`: the source directory path.
  • `dst`: the destination directory path.

Returns:

An error if the copy operation fails; nil on success.

Example:

if err := sysx.CopyDir("/home/user/docs", "/tmp/docs_bak"); err != nil {
    log.Fatal(err)
}

func CopyFile

func CopyFile(src, dst string) error

CopyFile copies the contents of the file at src to a new or truncated file at dst. The destination file is created with permission 0644 if it does not exist, or truncated if it does. The copy is performed using io.Copy with a buffered writer for efficiency.

Parameters:

  • `src`: the source file path to read from.
  • `dst`: the destination file path to write to.

Returns:

An error if the source cannot be opened, the destination cannot be
created, or the copy fails; nil on success.

Example:

if err := sysx.CopyFile("/etc/hosts", "/tmp/hosts.bak"); err != nil {
    log.Fatal(err)
}

func CountLines

func CountLines(path string) (int, error)

CountLines counts the number of lines in the file at path.

Counting is performed by reading the file in 32 KiB chunks and counting newline characters ("\n"), making it significantly faster than line-by-line scanning and avoiding line length limits. An empty file returns 0.

Parameters:

  • `path`: the file system path to count lines in.

Returns:

(int, error): the number of lines and nil on success, or 0 and a non-nil
error if the file cannot be opened or read.

Example:

n, err := sysx.CountLines("/var/log/app.log")
if err != nil {
    log.Fatal(err)
}
fmt.Printf("%d lines\n", n)

func CreateDir

func CreateDir(path string) error

CreateDir creates the named directory, along with any necessary parent directories, using permission 0755.

It is equivalent to os.MkdirAll(path, 0755) and is idempotent: calling it on a directory that already exists is a no-op.

Parameters:

  • `path`: the directory path to create.

Returns:

An error if the directory could not be created; nil on success.

Example:

if err := sysx.CreateDir("/tmp/app/logs"); err != nil {
    log.Fatal(err)
}

func CreateDirs added in v0.1.11

func CreateDirs(perm fs.FileMode, paths ...string) error

CreateDirs creates multiple directories with the given permission.

It is equivalent to os.MkdirAll for each directory and is idempotent: calling it on a directory that already exists is a no-op.

Parameters:

  • `perm`: the permission to use for creating the directories.
  • `paths`: the directory paths to create.

Returns:

An error if any of the directories could not be created; nil on success.

Example:

if err := sysx.CreateDirs(0o755, "/tmp/app/logs", "/tmp/app/cache"); err != nil {
    log.Fatal(err)
}

func CreateSubDirs added in v0.1.11

func CreateSubDirs(perm fs.FileMode, parent string, subdirs ...string) error

CreateSubDirs creates subdirectories under a parent directory with the given permission.

It is equivalent to os.MkdirAll for each subdirectory and is idempotent: calling it on a directory that already exists is a no-op.

Parameters:

  • `perm`: the permission to use for creating the directories.
  • `parentDir`: the parent directory path.
  • `subDirs`: the subdirectory names to create.

Returns:

An error if any of the subdirectories could not be created; nil on success.

Example:

if err := sysx.CreateSubDirs(0o755, "/tmp/app", "logs", "cache"); err != nil {
    log.Fatal(err)
}

func CurrentProcessName

func CurrentProcessName() string

CurrentProcessName returns the base name of the executable that started the current process.

The value is derived from os.Executable followed by filepath.Base. If the executable path cannot be determined, an empty string is returned.

Returns:

A string containing the base filename of the current executable.

Example:

fmt.Println(sysx.CurrentProcessName()) // e.g. "myapp"

func DirExists

func DirExists(path string) bool

DirExists reports whether a directory exists at the given path.

The function returns true only when the path exists and refers to a directory (not a regular file or other entry).

Parameters:

  • `path`: the file system path to check.

Returns:

A boolean value:
 - true  when a directory exists at path;
 - false otherwise.

Example:

if sysx.DirExists("/tmp") {
    fmt.Println("tmp dir exists")
}

func DirName

func DirName(path string) string

DirName returns all but the last element of path, typically the path's directory. The returned path does not end in a separator unless it is the root directory. If path is empty, DirName returns ".".

It wraps filepath.Dir and is safe for concurrent use.

Parameters:

  • `path`: the file system path.

Returns:

A string containing the directory component of path.

Example:

sysx.DirName("/etc/hosts")   // "/etc"
sysx.DirName("/usr/bin/git") // "/usr/bin"
sysx.DirName("file.txt")     // "."

func EnvMap

func EnvMap() map[string]string

EnvMap returns all current environment variables as a map from key to value.

Each entry in os.Environ is split on the first '=' character. Variables without a value portion are stored with an empty string value. The returned map is a snapshot; subsequent changes to the process environment are not reflected.

Returns:

A non-nil map[string]string containing all current environment variables.

Example:

env := sysx.EnvMap()
fmt.Println(env["HOME"])

func Environ

func Environ() []string

Environ returns a copy of all environment variables in the form "key=value".

It delegates directly to os.Environ. The returned slice is a snapshot; subsequent changes to the process environment are not reflected.

Returns:

A []string where each element is a "KEY=value" pair.

Example:

for _, e := range sysx.Environ() {
    fmt.Println(e)
}

func ExecAsync

func ExecAsync(name string, args ...string) (*exec.Cmd, error)

ExecAsync starts the named program asynchronously and returns the underlying *exec.Cmd without waiting for it to finish. The caller is responsible for calling cmd.Wait() to release associated resources and obtain the exit status.

Parameters:

  • `name`: the program name or path.
  • `args`: optional arguments.

Returns:

(*exec.Cmd, error): the started command handle and nil on success, or nil and a non-nil error if the command could not be started.

Example:

cmd, err := sysx.ExecAsync("long-running-server", "--port", "8080")

if err != nil {
   log.Fatal(err)
}

// ... do other work ... cmd.Wait()

func ExecCommand

func ExecCommand(name string, args ...string) error

ExecCommand runs the named program with the provided arguments and waits for it to complete. Both stdout and stderr are discarded.

The function does not use shell interpolation; name must be a program name or absolute path. An error is returned if name is empty, if the program cannot be found, or if the program exits with a non-zero status.

Parameters:

  • `name`: the program name or path to execute.
  • `args`: optional arguments to pass to the program.

Returns:

An error if the command could not be started or exited non-zero, or nil on success.

Example:

if err := sysx.ExecCommand("go", "build", "./..."); err != nil {
   log.Fatal(err)
}

func ExecCommandContext

func ExecCommandContext(ctx context.Context, name string, args ...string) error

ExecCommandContext runs the named program under the provided context. The command is cancelled when ctx is cancelled or its deadline expires.

Parameters:

  • `ctx`: the context controlling cancellation and deadline.
  • `name`: the program name or path.
  • `args`: optional arguments.

Returns:

An error if the command failed, was cancelled, or timed out; nil on success.

Example:

ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel()

if err := sysx.ExecCommandContext(ctx, "go", "test", "./..."); err != nil {
   log.Fatal(err)
}

func ExecCommandInDir

func ExecCommandInDir(dir, name string, args ...string) error

ExecCommandInDir runs the named program with the provided arguments from the specified working directory and waits for it to complete.

stdout and stderr are discarded. The function does not use shell interpolation.

Parameters:

  • `dir`: the working directory in which to run the command.
  • `name`: the program name or path to execute.
  • `args`: optional arguments to pass to the program.

Returns:

An error if the command could not be started or exited non-zero, or nil on success.

Example:

err := sysx.ExecCommandInDir("/tmp/myproject", "go", "test", "./...")

func ExecCommandWithTimeout

func ExecCommandWithTimeout(timeout time.Duration, name string, args ...string) error

ExecCommandWithTimeout runs the named program with the provided arguments and a deadline of timeout. If the command does not finish within the deadline the process is killed and a context deadline-exceeded error is returned.

Parameters:

  • `timeout`: maximum duration to wait for the command to complete.
  • `name`: the program name or path to execute.
  • `args`: optional arguments to pass to the program.

Returns:

An error if the command timed out, could not be started, or exited non-zero, or nil on success.

Example:

err := sysx.ExecCommandWithTimeout(5*time.Second, "ping", "-c", "1", "localhost")

func ExecOutput

func ExecOutput(name string, args ...string) (string, error)

ExecOutput runs the named program with the provided arguments, waits for it to complete, and returns the combined stdout and stderr output as a string.

The function does not use shell interpolation. An error is returned if name is empty, if the program cannot be found, or if the program exits with a non-zero status.

Parameters:

  • `name`: the program name or path to execute.
  • `args`: optional arguments to pass to the program.

Returns:

(string, error): the combined output and nil on success, or the partial output and a non-nil error on failure.

Example:

out, err := sysx.ExecOutput("git", "rev-parse", "HEAD")

if err != nil {
   log.Fatal(err)
}

fmt.Println(strings.TrimSpace(out))

func ExecOutputInDir

func ExecOutputInDir(dir, name string, args ...string) (string, error)

ExecOutputInDir runs the named program with the provided arguments from the specified working directory, waits for it to complete, and returns the combined stdout and stderr output.

Parameters:

  • `dir`: the working directory in which to run the command.
  • `name`: the program name or path to execute.
  • `args`: optional arguments to pass to the program.

Returns:

(string, error): the combined output and nil on success, or the partial output and a non-nil error on failure.

Example:

out, err := sysx.ExecOutputInDir("/tmp/myproject", "git", "status")

func ExecOutputLines

func ExecOutputLines(name string, args ...string) ([]string, error)

ExecOutputLines runs the named program and returns its stdout split into individual lines. Line endings are stripped; empty lines are preserved. Stderr is captured but discarded on success. On failure, the error from the command is returned.

Parameters:

  • `name`: the program name or path.
  • `args`: optional arguments.

Returns:

([]string, error): lines of stdout and nil on success, or nil and a non-nil error on failure.

Example:

lines, err := sysx.ExecOutputLines("git", "log", "--oneline", "-5")

for _, l := range lines {
   fmt.Println(l)
}

func ExecOutputWithTimeout

func ExecOutputWithTimeout(timeout time.Duration, name string, args ...string) (string, error)

ExecOutputWithTimeout runs the named program with the provided arguments and a deadline of timeout, returning the combined stdout and stderr.

If the command does not finish within the deadline the process is killed and a context deadline-exceeded error is returned along with any output produced before the timeout.

Parameters:

  • `timeout`: maximum duration to wait for the command to complete.
  • `name`: the program name or path to execute.
  • `args`: optional arguments to pass to the program.

Returns:

(string, error): the combined output and nil on success, or partial output and a non-nil error on failure or timeout.

Example:

out, err := sysx.ExecOutputWithTimeout(3*time.Second, "curl", "-s", "http://localhost")

func ExecPipeline

func ExecPipeline(commands ...[]string) (string, error)

ExecPipeline executes a sequence of commands as a shell pipeline: the standard output of each command is connected to the standard input of the next. The stdout of the final command is returned as a string.

Each element of commands is a []string where the first element is the program name and the remaining elements are its arguments. An error is returned if any command in the pipeline fails to start or exits non-zero.

Parameters:

  • `commands`: one or more [name, arg...] command descriptors.

Returns:

(string, error): the stdout of the last command and nil on success, or partial output and a non-nil error on failure.

Example:

out, err := sysx.ExecPipeline(

[]string{"cat", "/etc/passwd"},
[]string{"grep", "root"},
[]string{"cut", "-d:", "-f1"},

)

func ExecStreaming

func ExecStreaming(stdout, stderr io.Writer, name string, args ...string) error

ExecStreaming runs the named program, forwarding its stdout and stderr to the provided writers in real time as the command executes. Either writer may be nil to discard the corresponding stream.

Parameters:

  • `stdout`: writer receiving standard output; nil to discard.
  • `stderr`: writer receiving standard error; nil to discard.
  • `name`: the program name or path.
  • `args`: optional arguments.

Returns:

An error if the command failed or could not be started; nil on success.

Example:

err := sysx.ExecStreaming(os.Stdout, os.Stderr, "go", "build", "./...")

func ExecutablePath

func ExecutablePath() (string, error)

ExecutablePath returns the path name for the executable that started the current process.

It delegates to os.Executable. The path is not guaranteed to be an absolute path on all platforms; callers requiring a canonical path should call filepath.EvalSymlinks on the result.

Returns:

(string, error): the path and nil on success, or an empty string and a
non-nil error on failure.

Example:

path, err := sysx.ExecutablePath()
if err != nil {
    log.Fatal(err)
}
fmt.Println(path)

func Ext

func Ext(path string) string

Ext returns the file name extension used by path. The extension is the suffix beginning at the final dot in the last element of path; it is empty if there is no dot.

It wraps filepath.Ext and is safe for concurrent use.

Parameters:

  • `path`: the file system path.

Returns:

A string containing the extension (including the leading dot), or an
empty string when path has no extension.

Example:

sysx.Ext("archive.tar.gz") // ".gz"
sysx.Ext("/etc/hosts")     // ""
sysx.Ext("README.md")      // ".md"

func Extname added in v0.1.11

func Extname(path string) string

Extname returns the file extension without the leading dot.

Parameters:

  • `path`: the file system path.

Returns:

A string containing the file extension without the leading dot, or an
empty string when path has no extension.

Example:

sysx.Extname("archive.tar.gz") // "gz"
sysx.Extname("/etc/hosts")     // ""
sysx.Extname("README.md")      // "md"

func FileExists

func FileExists(path string) bool

FileExists reports whether a file exists at the given path.

The function returns true for any path that exists in the file system, including directories and symbolic links. Use IsFile to restrict the check to regular files.

Parameters:

  • `path`: the file system path to check.

Returns:

A boolean value:
 - true  when any file system entry exists at path;
 - false when the path does not exist or the existence cannot be determined.

Example:

if sysx.FileExists("/etc/hosts") {
    fmt.Println("hosts file found")
}

func FileMD5 added in v0.1.11

func FileMD5(path string) (string, error)

FileMD5 calculates the MD5 checksum of the file at the given path and returns it as a lowercase hexadecimal string.

Parameters:

  • `path`: the file system path to hash.

Returns:

(string, error): the MD5 hash and nil on success, or an empty string and
a non-nil error if the file cannot be opened or read.

Example:

hash, err := sysx.FileMD5("/etc/hosts")
if err != nil {
    log.Fatal(err)
}
fmt.Println(hash)

func FileModTime

func FileModTime(path string) (time.Time, error)

FileModTime returns the modification time of the file at the given path.

Symbolic links are followed; the modification time of the link target is returned.

Parameters:

  • `path`: the file system path to inspect.

Returns:

(time.Time, error): the modification time and nil on success, or the zero
time and a non-nil error if the file does not exist or cannot be stat'd.

Example:

t, err := sysx.FileModTime("/var/log/syslog")
if err != nil {
    log.Fatal(err)
}
fmt.Println(t.Format(time.RFC3339))

func FileMode

func FileMode(path string) (os.FileMode, error)

FileMode returns the permission bits (os.FileMode) of the file at the given path.

Symbolic links are followed; the mode of the link target is returned.

Parameters:

  • `path`: the file system path to inspect.

Returns:

(os.FileMode, error): the file permission bits and nil on success, or 0
and a non-nil error if the file does not exist or cannot be stat'd.

Example:

mode, err := sysx.FileMode("/etc/passwd")
if err != nil {
    log.Fatal(err)
}
fmt.Printf("%o\n", mode) // e.g. "644"

func FileSHA256 added in v0.1.11

func FileSHA256(path string) (string, error)

FileSHA256 calculates the SHA256 checksum of the file at the given path and returns it as a lowercase hexadecimal string.

Parameters:

  • `path`: the file system path to hash.

Returns:

(string, error): the SHA256 hash and nil on success, or an empty string
and a non-nil error if the file cannot be opened or read.

Example:

hash, err := sysx.FileSHA256("/etc/hosts")
if err != nil {
    log.Fatal(err)
}
fmt.Println(hash)

func FileSize

func FileSize(path string) (int64, error)

FileSize returns the size of the file at the given path in bytes.

Parameters:

  • `path`: the file system path to inspect.

Returns:

(int64, error): the file size in bytes and nil on success, or 0 and a
non-nil error if the file does not exist or cannot be stat'd.

Example:

size, err := sysx.FileSize("/var/log/syslog")
if err != nil {
    log.Fatal(err)
}
fmt.Printf("%d bytes\n", size)

func FindProcessByPID

func FindProcessByPID(pid int) (*os.Process, error)

FindProcessByPID returns the os.Process associated with the given PID.

On Unix-like systems, os.FindProcess always succeeds for any integer PID. Use ProcessExists to verify that the process is actually running before operating on the returned value.

Parameters:

  • `pid`: the process identifier to look up.

Returns:

(*os.Process, error): the process handle and nil on success, or nil and a
non-nil error if the lookup fails.

Example:

proc, err := sysx.FindProcessByPID(os.Getpid())
if err != nil {
    log.Fatal(err)
}
fmt.Println(proc.Pid)

func GID

func GID() int

GID returns the numeric group identifier of the calling process.

On Windows, it always returns -1 (os.Getgid is unsupported there).

Returns:

An int representing the current group identifier.

Example:

fmt.Println("GID:", sysx.GID())

func GetInterfaceIPs

func GetInterfaceIPs() ([]string, error)

GetInterfaceIPs returns all unicast IP addresses assigned to the machine's network interfaces, including both IPv4 and IPv6 addresses.

Loopback and down interfaces are excluded. The returned strings are in their canonical form (e.g. "192.168.1.5" or "fe80::1").

Returns:

([]string, error): a slice of IP address strings and nil on success, or
nil and a non-nil error when interface enumeration fails.

Example:

ips, err := sysx.GetInterfaceIPs()
for _, ip := range ips {
    fmt.Println(ip)
}

func GetLocalIP

func GetLocalIP() (string, error)

GetLocalIP returns the first non-loopback, non-link-local IPv4 address found on the machine's network interfaces.

The function iterates through all network interfaces in the order returned by net.Interfaces() and returns the first eligible address. An error is returned when no suitable address is found or interface enumeration fails.

Returns:

(string, error): the IPv4 address string and nil on success, or an empty
string and a non-nil error when no local IP can be determined.

Example:

ip, err := sysx.GetLocalIP()
if err != nil {
    log.Fatal(err)
}
fmt.Println("local IP:", ip)

func GetPublicIP

func GetPublicIP() (string, error)

GetPublicIP retrieves the public (externally visible) IP address of the machine by querying the well-known plain-text endpoint https://api.ipify.org.

A 10-second timeout is applied to the HTTP request. The function requires outbound internet access; it will fail in air-gapped environments.

Returns:

(string, error): the public IP address string and nil on success, or an
empty string and a non-nil error when the request fails or times out.

Example:

ip, err := sysx.GetPublicIP()
if err != nil {
    log.Printf("cannot determine public IP: %v", err)
} else {
    fmt.Println("public IP:", ip)
}

func Getenv

func Getenv(key, fallback string) string

Getenv returns the value of the environment variable named by key.

If the variable is not set, or is set to an empty string, Getenv returns the provided fallback value instead.

Parameters:

  • `key`: the name of the environment variable.
  • `fallback`: the value to return when the variable is absent or empty.

Returns:

A string: either the current value of the variable or fallback.

Example:

port := sysx.Getenv("PORT", "8080")

func GetenvBool

func GetenvBool(key string, defaultValue bool) bool

GetenvBool returns the value of the environment variable named by key parsed as a boolean.

The following string values (case-insensitive) are recognised:

  • true strings: "1", "true", "yes", "on"
  • false strings: "0", "false", "no", "off"

If the variable is not set, is empty, or contains an unrecognised value, the provided fallback value is returned.

Parameters:

  • `key`: the name of the environment variable.
  • `fallback`: the boolean to return when the variable cannot be resolved.

Returns:

A bool: either the parsed variable value or fallback.

Example:

debug := sysx.GetenvBool("DEBUG", false)

func GetenvDuration

func GetenvDuration(key string, defaultValue time.Duration) time.Duration

GetenvDuration returns the value of the environment variable named by key parsed as a duration.

If the variable is not set, is empty, or cannot be parsed as a duration, the provided fallback value is returned.

Parameters:

  • `key`: the name of the environment variable.
  • `fallback`: the duration to return when the variable cannot be resolved.

Returns:

A time.Duration: either the parsed variable value or fallback.

Example:

workers := sysx.GetenvDuration("WORKERS", 4)

func GetenvFloat32

func GetenvFloat32(key string, defaultValue float32) float32

GetenvFloat32 returns the value of the environment variable named by key parsed as a float32.

If the variable is not set, is empty, or cannot be parsed as a float32, the provided fallback value is returned.

Parameters:

  • `key`: the name of the environment variable.
  • `fallback`: the float32 to return when the variable is absent, empty, or non-numeric.

Returns:

A float32: either the parsed variable value or fallback.

Example:

workers := sysx.GetenvFloat32("WORKERS", 4)

func GetenvFloat64

func GetenvFloat64(key string, defaultValue float64) float64

GetenvFloat64 returns the value of the environment variable named by key parsed as a float64.

If the variable is not set, is empty, or cannot be parsed as a float64, the provided fallback value is returned.

Parameters:

  • `key`: the name of the environment variable.
  • `fallback`: the float64 to return when the variable is absent, empty, or non-numeric.

Returns:

A float64: either the parsed variable value or fallback.

Example:

workers := sysx.GetenvFloat64("WORKERS", 4)

func GetenvInt

func GetenvInt(key string, defaultValue int) int

GetenvInt returns the value of the environment variable named by key parsed as a base-10 integer.

If the variable is not set, is empty, or cannot be parsed as an integer, the provided fallback value is returned.

Parameters:

  • `key`: the name of the environment variable.
  • `fallback`: the integer to return when the variable is absent, empty, or non-numeric.

Returns:

An int: either the parsed variable value or fallback.

Example:

workers := sysx.GetenvInt("WORKERS", 4)

func GetenvInt8

func GetenvInt8(key string, defaultValue int8) int8

GetenvInt8 returns the value of the environment variable named by key parsed as a base-10 integer.

If the variable is not set, is empty, or cannot be parsed as an integer, the provided fallback value is returned.

Parameters:

  • `key`: the name of the environment variable.
  • `fallback`: the integer to return when the variable is absent, empty, or non-numeric.

Returns:

An int8: either the parsed variable value or fallback.

Example:

workers := sysx.GetenvInt8("WORKERS", 4)

func GetenvInt16

func GetenvInt16(key string, defaultValue int16) int16

GetenvInt16 returns the value of the environment variable named by key parsed as a base-10 integer.

If the variable is not set, is empty, or cannot be parsed as an integer, the provided fallback value is returned.

Parameters:

  • `key`: the name of the environment variable.
  • `fallback`: the integer to return when the variable is absent, empty, or non-numeric.

Returns:

An int16: either the parsed variable value or fallback.

Example:

workers := sysx.GetenvInt16("WORKERS", 4)

func GetenvInt32

func GetenvInt32(key string, defaultValue int32) int32

GetenvInt32 returns the value of the environment variable named by key parsed as a base-10 integer.

If the variable is not set, is empty, or cannot be parsed as an integer, the provided fallback value is returned.

Parameters:

  • `key`: the name of the environment variable.
  • `fallback`: the integer to return when the variable is absent, empty, or non-numeric.

Returns:

An int32: either the parsed variable value or fallback.

Example:

workers := sysx.GetenvInt32("WORKERS", 4)

func GetenvInt64

func GetenvInt64(key string, defaultValue int64) int64

GetenvInt64 returns the value of the environment variable named by key parsed as a base-10 integer.

If the variable is not set, is empty, or cannot be parsed as an integer, the provided fallback value is returned.

Parameters:

  • `key`: the name of the environment variable.
  • `fallback`: the integer to return when the variable is absent, empty, or non-numeric.

Returns:

An int64: either the parsed variable value or fallback.

Example:

workers := sysx.GetenvInt64("WORKERS", 4)

func GetenvSlice

func GetenvSlice(key, sep string) []string

GetenvSlice returns the value of the environment variable named by key split by the provided separator.

If the variable is not set or is empty, a nil slice is returned. Empty elements produced by consecutive separators are preserved in the result.

Parameters:

  • `key`: the name of the environment variable.
  • `sep`: the separator string used to split the variable's value.

Returns:

A []string containing the split elements, or nil when the variable is absent or empty.

Example:

hosts := sysx.GetenvSlice("HOSTS", ",") // ["a.example.com", "b.example.com"]

func GetenvUint

func GetenvUint(key string, defaultValue uint) uint

GetenvUint returns the value of the environment variable named by key parsed as a base-10 unsigned integer.

If the variable is not set, is empty, or cannot be parsed as an integer, the provided fallback value is returned.

Parameters:

  • `key`: the name of the environment variable.
  • `fallback`: the integer to return when the variable is absent, empty, or non-numeric.

Returns:

An uint: either the parsed variable value or fallback.

Example:

workers := sysx.GetenvUint("WORKERS", 4)

func GetenvUint8

func GetenvUint8(key string, defaultValue uint8) uint8

GetenvUint8 returns the value of the environment variable named by key parsed as a base-10 unsigned integer.

If the variable is not set, is empty, or cannot be parsed as an integer, the provided fallback value is returned.

Parameters:

  • `key`: the name of the environment variable.
  • `fallback`: the integer to return when the variable is absent, empty, or non-numeric.

Returns:

An uint8: either the parsed variable value or fallback.

Example:

workers := sysx.GetenvUint8("WORKERS", 4)

func GetenvUint16

func GetenvUint16(key string, defaultValue uint16) uint16

GetenvUint16 returns the value of the environment variable named by key parsed as a base-10 unsigned integer.

If the variable is not set, is empty, or cannot be parsed as an integer, the provided fallback value is returned.

Parameters:

  • `key`: the name of the environment variable.
  • `fallback`: the integer to return when the variable is absent, empty, or non-numeric.

Returns:

An uint16: either the parsed variable value or fallback.

Example:

workers := sysx.GetenvUint16("WORKERS", 4)

func GetenvUint32

func GetenvUint32(key string, defaultValue uint32) uint32

GetenvUint32 returns the value of the environment variable named by key parsed as a base-10 unsigned integer.

If the variable is not set, is empty, or cannot be parsed as an integer, the provided fallback value is returned.

Parameters:

  • `key`: the name of the environment variable.
  • `fallback`: the integer to return when the variable is absent, empty, or non-numeric.

Returns:

An uint32: either the parsed variable value or fallback.

Example:

workers := sysx.GetenvUint32("WORKERS", 4)

func GetenvUint64

func GetenvUint64(key string, defaultValue uint64) uint64

GetenvUint64 returns the value of the environment variable named by key parsed as a base-10 unsigned integer.

If the variable is not set, is empty, or cannot be parsed as an integer, the provided fallback value is returned.

Parameters:

  • `key`: the name of the environment variable.
  • `fallback`: the integer to return when the variable is absent, empty, or non-numeric.

Returns:

An uint64: either the parsed variable value or fallback.

Example:

workers := sysx.GetenvUint64("WORKERS", 4)

func GoVersion

func GoVersion() string

GoVersion returns the Go runtime version string of the binary.

The value is identical to runtime.Version() and has the form "go1.X.Y" (e.g. "go1.24.0").

Returns:

A non-empty string identifying the Go version used to build the binary.

Example:

fmt.Println(sysx.GoVersion()) // "go1.24.0"

func Hasenv

func Hasenv(key string) bool

Hasenv reports whether the environment variable named by key exists and is non-empty.

A variable that is explicitly set to an empty string is treated as absent for the purposes of this function.

Parameters:

  • `key`: the name of the environment variable.

Returns:

A boolean value:
 - true  when the variable is set and its value is non-empty;
 - false otherwise.

Example:

if sysx.Hasenv("DEBUG") {
    fmt.Println("debug mode enabled")
}
func Head(path string, n int) ([]string, error)

Head reads the first n lines of the file at path and returns them as a slice of strings with line endings stripped. If the file has fewer than n lines, all lines are returned. n must be non-negative; passing 0 returns an empty (non-nil) slice with no error.

Parameters:

  • `path`: the file system path to read.
  • `n`: the maximum number of lines to return.

Returns:

([]string, error): up to n lines and nil on success, or a partial result
and a non-nil error on failure.

Example:

lines, err := sysx.Head("/var/log/app.log", 10)
for _, l := range lines {
    fmt.Println(l)
}

func HomeDir

func HomeDir() (string, error)

HomeDir returns the current user's home directory.

The lookup is performed via os/user.Current(). On most systems this respects the HOME environment variable.

Returns:

(string, error): the home directory path and nil on success, or an empty
string and a non-nil error if the current user cannot be determined.

Example:

home, err := sysx.HomeDir()
if err != nil {
    log.Fatal(err)
}
fmt.Println(home)

func Hostname

func Hostname() (string, error)

Hostname returns the host name reported by the kernel.

It delegates directly to os.Hostname and propagates any error returned by the operating system.

Returns:

(string, error): the hostname and nil on success, or an empty string and
a non-nil error on failure.

Example:

name, err := sysx.Hostname()
if err != nil {
    log.Fatal(err)
}
fmt.Println(name)

func Is64Bit

func Is64Bit() bool

Is64Bit reports whether the program is running on a 64-bit architecture.

The check inspects runtime.GOARCH for the well-known 64-bit identifiers: "amd64", "arm64", "ppc64", "ppc64le", "mips64", "mips64le", "s390x", "riscv64", and "wasm". Any architecture not in this list is treated as non-64-bit.

Returns:

A boolean value:
 - true  when the architecture is 64-bit;
 - false otherwise.

Example:

if sysx.Is64Bit() {
    fmt.Println("64-bit architecture")
}

func IsAbsPath added in v0.1.11

func IsAbsPath(path string) bool

IsAbsPath returns true if the path is absolute.

It wraps filepath.IsAbs and is safe for concurrent use.

Parameters:

  • `path`: the file system path to check.

Returns:

bool: true if the path is absolute, false otherwise.

Example:

sysx.IsAbsPath("/etc/hosts")   // true
sysx.IsAbsPath("relative/dir") // false

func IsArm

func IsArm() bool

IsArm reports whether the program is running on an ARM-based architecture.

Both 32-bit ARM ("arm") and 64-bit AArch64 ("arm64") targets are considered ARM-based.

Returns:

A boolean value:
 - true  when the architecture is "arm" or "arm64";
 - false otherwise.

Example:

if sysx.IsArm() {
    fmt.Println("ARM architecture")
}

func IsBinary added in v0.1.11

func IsBinary(path string) (bool, error)

IsBinary reports whether the file at the given path appears to be a binary file.

Detection is performed using a heuristic: the first 8 KiB of the file are searched for a null byte (0x00). If one is found, the file is considered binary. An empty file is not considered binary.

Parameters:

  • `path`: the file system path to check.

Returns:

(bool, error): true if the file is binary and nil on success, or false
and a non-nil error if the file cannot be opened or read.

Example:

isBin, err := sysx.IsBinary("/usr/bin/ls")
if err == nil && isBin {
    fmt.Println("binary file detected")
}

func IsDarwin

func IsDarwin() bool

IsDarwin reports whether the current operating system is macOS (Darwin).

The check is performed at runtime by comparing runtime.GOOS against the string "darwin". The function is safe for concurrent use and allocates no memory.

Returns:

A boolean value:
 - true  when the program is running on macOS/Darwin;
 - false on any other operating system.

Example:

if sysx.IsDarwin() {
    fmt.Println("running on macOS")
}

func IsDir

func IsDir(path string) bool

IsDir reports whether the given path exists and is a directory.

Symbolic links are followed; if the link target is a directory this function returns true.

Parameters:

  • `path`: the file system path to check.

Returns:

A boolean value:
 - true  when path exists and is a directory;
 - false otherwise.

Example:

if sysx.IsDir("/home") {
    fmt.Println("is a directory")
}

func IsDirEmpty added in v0.1.11

func IsDirEmpty(path string) (bool, error)

IsDirEmpty reports whether the directory at path exists and contains no entries.

Parameters:

  • `path`: the directory path to check.

Returns:

(bool, error): true if the directory is empty and nil on success, or false
and a non-nil error if the directory does not exist or cannot be read.

Example:

empty, err := sysx.IsDirEmpty("/tmp/empty_dir")
if err == nil && empty {
    fmt.Println("directory is empty")
}

func IsExecutable

func IsExecutable(path string) bool

IsExecutable reports whether the file at the given path is executable by its owner.

The check is based on file mode bits (0100). On Windows, it verifies if the file ends with a known executable extension or matches the PATHEXT environment variable.

Parameters:

  • `path`: the file system path to check.

Returns:

A boolean value:
 - true  when the file exists and its owner-execute bit is set (or is an executable on Windows);
 - false otherwise.

Example:

if sysx.IsExecutable("/usr/bin/git") {
    fmt.Println("git is executable")
}

func IsFile

func IsFile(path string) bool

IsFile reports whether the given path exists and is a regular file.

Symbolic links are followed; if the link target is a regular file this function returns true.

Parameters:

  • `path`: the file system path to check.

Returns:

A boolean value:
 - true  when path exists and is a regular file;
 - false otherwise.

Example:

if sysx.IsFile("/etc/passwd") {
    fmt.Println("regular file")
}

func IsIPv4

func IsIPv4(ip string) bool

IsIPv4 reports whether the given string is a valid IPv4 address.

The check uses net.ParseIP: the string must be a dotted-decimal notation such as "192.168.1.1". IPv4-in-IPv6 forms (e.g. "::ffff:192.168.1.1") are NOT recognized as IPv4 by this function.

Parameters:

  • `ip`: the IP address string to validate.

Returns:

A boolean value:
 - true  when ip is a valid, pure IPv4 address;
 - false otherwise.

Example:

sysx.IsIPv4("192.168.1.1") // true
sysx.IsIPv4("::1")         // false

func IsIPv6

func IsIPv6(ip string) bool

IsIPv6 reports whether the given string is a valid IPv6 address.

Addresses that are valid IPv4 dotted-decimal strings (e.g. "192.168.1.1") are NOT considered IPv6 by this function, even though Go internally represents IPv4 addresses using a 16-byte form.

Parameters:

  • `ip`: the IP address string to validate.

Returns:

A boolean value:
 - true  when ip is a valid IPv6 address (and not a pure IPv4 string);
 - false otherwise.

Example:

sysx.IsIPv6("::1")           // true
sysx.IsIPv6("2001:db8::1")  // true
sysx.IsIPv6("192.168.1.1")  // false

func IsLinux

func IsLinux() bool

IsLinux reports whether the current operating system is Linux.

The check is performed at runtime by comparing runtime.GOOS against the string "linux". The function is safe for concurrent use and allocates no memory.

Returns:

A boolean value:
 - true  when the program is running on Linux;
 - false on any other operating system.

Example:

if sysx.IsLinux() {
    fmt.Println("running on Linux")
}

func IsLocalIP

func IsLocalIP(ip string) bool

IsLocalIP reports whether the given IP address is a loopback or private (RFC 1918 / RFC 4193) address.

Recognized local ranges:

  • IPv4 loopback: 127.0.0.0/8
  • IPv4 link-local: 169.254.0.0/16
  • IPv4 private: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
  • IPv6 loopback: ::1
  • IPv6 link-local: fe80::/10
  • IPv6 unique-local: fc00::/7

Parameters:

  • `ip`: the IP address string to check.

Returns:

A boolean value:
 - true  when the address belongs to a local/private range;
 - false when the address is public or cannot be parsed.

Example:

sysx.IsLocalIP("127.0.0.1")    // true
sysx.IsLocalIP("10.0.0.1")     // true
sysx.IsLocalIP("8.8.8.8")      // false

func IsPortAvailable

func IsPortAvailable(port int) bool

IsPortAvailable reports whether the given TCP port is available for binding on the local machine (i.e. nothing is currently listening on that port).

The check attempts to open a TCP listener on 0.0.0.0:<port>. If the listen succeeds the port is free and the listener is immediately closed.

Parameters:

  • `port`: the TCP port number to test (1-65535).

Returns:

A boolean value:
 - true  when the port is not in use and can be bound;
 - false when the port is already in use, the OS rejects the bind, or
         the port number is outside the valid range 1-65535.

Example:

if sysx.IsPortAvailable(8080) {
    fmt.Println("port 8080 is free")
}

func IsPortOpen

func IsPortOpen(host string, port int) bool

IsPortOpen reports whether the given TCP port is reachable on host.

The check attempts a TCP connection with a 3-second timeout. The function returns true only when the connection is accepted; no data is exchanged.

Parameters:

  • `host`: the host name or IP address to connect to.
  • `port`: the TCP port number (1-65535).

Returns:

A boolean value:
 - true  when a TCP connection to host:port succeeds within 3 seconds;
 - false otherwise.

Example:

if sysx.IsPortOpen("localhost", 5432) {
    fmt.Println("PostgreSQL is reachable")
}

func IsPrivileged

func IsPrivileged() bool

IsPrivileged reports whether the current process is running with root (super-user) privileges.

On Unix-like systems this is determined by checking whether the effective user identifier is 0. On Windows, UID() returns -1 and this function always returns false; use the Windows API for accurate privilege checks.

Returns:

A boolean value:
 - true  when UID() == 0 (running as root on Unix);
 - false otherwise.

Example:

if sysx.IsPrivileged() {
    fmt.Println("running as root")
}

func IsReadable

func IsReadable(path string) bool

IsReadable reports whether the file at the given path is readable by its owner.

The check is based on file mode bits (0400). On Windows, mode bits are an approximation.

Parameters:

  • `path`: the file system path to check.

Returns:

A boolean value:
 - true  when the file exists and its owner-read bit is set;
 - false otherwise.

Example:

if sysx.IsReadable("/etc/hosts") {
    fmt.Println("readable")
}

func IsSafeDirEmpty added in v0.1.11

func IsSafeDirEmpty(path string) bool

IsSafeDirEmpty reports whether the directory at path exists and contains no entries.

Parameters:

  • `path`: the directory path to check.

Returns:

true if the directory is empty and nil on success, or false if the directory does not exist or cannot be read.

Example:

empty := sysx.IsSafeDirEmpty("/tmp/empty_dir")
if empty {
    fmt.Println("directory is empty")
}
func IsSymlink(path string) bool

IsSymlink reports whether the given path is a symbolic link.

Unlike IsFile and IsDir, this function does NOT follow symbolic links; it inspects the link entry itself using os.Lstat.

Parameters:

  • `path`: the file system path to check.

Returns:

A boolean value:
 - true  when path exists and is a symbolic link;
 - false otherwise.

Example:

if sysx.IsSymlink("/usr/bin/python") {
    fmt.Println("python is a symlink")
}

func IsTTY

func IsTTY(w io.Writer) bool

IsTTY reports whether w is connected to a terminal (character device).

Parameters:

  • `w`: the writer to test

Returns:

true when w is an *os.File whose device mode includes os.ModeCharDevice.

func IsValidHost

func IsValidHost(host string) bool

IsValidHost reports whether the given string is a resolvable host name or a valid IP address.

The function first tries to parse the input as an IP address. If that fails, it attempts a DNS lookup. It returns true if either check succeeds.

Parameters:

  • `host`: the host name or IP address to validate.

Returns:

A boolean value:
 - true  when the host is a valid IP or can be resolved via DNS;
 - false otherwise.

Example:

sysx.IsValidHost("localhost")   // true (resolves to 127.0.0.1)
sysx.IsValidHost("8.8.8.8")    // true (valid IP)
sysx.IsValidHost("invalid..x") // false

func IsValidURL

func IsValidURL(rawURL string) bool

IsValidURL reports whether the given string is a syntactically valid URL with a non-empty scheme and host component.

The function uses url.Parse for parsing. It does not perform DNS resolution or check whether the URL is reachable.

Parameters:

  • `rawURL`: the URL string to validate.

Returns:

A boolean value:
 - true  when rawURL has a valid scheme and non-empty host;
 - false otherwise.

Example:

sysx.IsValidURL("https://example.com/path") // true
sysx.IsValidURL("not-a-url")                // false

func IsWindows

func IsWindows() bool

IsWindows reports whether the current operating system is Windows.

The check is performed at runtime by comparing runtime.GOOS against the string "windows". The function is safe for concurrent use and allocates no memory.

Returns:

A boolean value:
 - true  when the program is running on Windows;
 - false on any other operating system.

Example:

if sysx.IsWindows() {
    fmt.Println("running on Windows")
}

func IsWritable

func IsWritable(path string) bool

IsWritable reports whether the file at the given path can be opened for writing by the current process.

The check attempts to open the file with os.O_WRONLY. This approach respects ACLs and is more accurate than mode-bit inspection alone.

Parameters:

  • `path`: the file system path to check.

Returns:

A boolean value:
 - true  when the file exists and can be opened for writing;
 - false otherwise.

Example:

if sysx.IsWritable("/tmp/out.log") {
    fmt.Println("writable")
}

func JSONEnvMap

func JSONEnvMap() string

JSONEnvMap returns all current environment variables as a JSON string.

It delegates directly to os.Environ. The returned slice is a snapshot; subsequent changes to the process environment are not reflected.

Returns:

A string containing the JSON representation of all current environment variables.

Example:

env := sysx.JSONEnvMap()
fmt.Println(env)

func JoinPath

func JoinPath(elem ...string) string

JoinPath joins any number of path elements into a single path, separating them with the OS-specific path separator. Empty elements are ignored. The result is cleaned via filepath.Clean. An empty result is returned when all elements are empty.

It wraps filepath.Join and is safe for concurrent use.

Parameters:

  • `elem`: zero or more path components to join.

Returns:

A string containing the joined and cleaned path.

Example:

sysx.JoinPath("/usr", "local", "bin") // "/usr/local/bin"
sysx.JoinPath("a", "b", "c")          // "a/b/c" (on Unix)

func KillProcess

func KillProcess(pid int) error

KillProcess sends SIGTERM to the process identified by pid.

SIGTERM is the standard termination signal that gives the process an opportunity to clean up before exiting. Use KillProcessForcefully when an immediate kill is required.

Parameters:

  • `pid`: the process identifier of the target process.

Returns:

An error if the process could not be found or signalled, or nil on success.

Example:

if err := sysx.KillProcess(pid); err != nil {
    log.Printf("failed to SIGTERM process %d: %v", pid, err)
}

func KillProcessForcefully

func KillProcessForcefully(pid int) error

KillProcessForcefully sends SIGKILL to the process identified by pid.

SIGKILL cannot be caught or ignored by the target process; it is terminated immediately. Prefer KillProcess (SIGTERM) when a graceful shutdown is possible.

Parameters:

  • `pid`: the process identifier of the target process.

Returns:

An error if the process could not be found or signalled, or nil on success.

Example:

if err := sysx.KillProcessForcefully(pid); err != nil {
    log.Printf("failed to SIGKILL process %d: %v", pid, err)
}

func ListDir

func ListDir(path string) ([]string, error)

ListDir returns the names of all entries (files, directories, symlinks) in the directory at path, in the order returned by os.ReadDir (lexicographic order). Only entry names are returned; use filepath.Join to build full paths.

Parameters:

  • `path`: the directory path to read.

Returns:

([]string, error): the entry names and nil on success, or nil and a
non-nil error if the directory cannot be read.

Example:

names, err := sysx.ListDir("/etc")
for _, name := range names {
    fmt.Println(name)
}

func ListDirDirs

func ListDirDirs(path string) ([]string, error)

ListDirDirs returns only the names of subdirectories inside the directory at path. Regular files, symbolic links, and other non-directory entries are excluded. Entries are returned in lexicographic order.

Parameters:

  • `path`: the directory path to read.

Returns:

([]string, error): the subdirectory names and nil on success, or nil and
a non-nil error if the directory cannot be read.

Example:

dirs, err := sysx.ListDirDirs("/home")
for _, d := range dirs {
    fmt.Println(d)
}

func ListDirFiles

func ListDirFiles(path string) ([]string, error)

ListDirFiles returns only the names of regular files in the directory at path. Directories, symbolic links, and other non-regular entries are excluded. Entries are returned in lexicographic order.

Parameters:

  • `path`: the directory path to read.

Returns:

([]string, error): the file names and nil on success, or nil and a
non-nil error if the directory cannot be read.

Example:

files, err := sysx.ListDirFiles("/var/log")
for _, f := range files {
    fmt.Println(f)
}

func MemStats

func MemStats() runtime.MemStats

MemStats returns a snapshot of the Go runtime memory allocator statistics for the current process.

The snapshot is obtained by calling runtime.ReadMemStats, which stops the world briefly on older Go versions. On Go 1.16+, the stop-the-world pause is significantly shorter.

Returns:

A runtime.MemStats value populated with the current memory statistics.

Example:

stats := sysx.MemStats()
fmt.Printf("heap alloc: %d bytes\n", stats.HeapAlloc)

func MimeFromName added in v0.1.24

func MimeFromName(name string) string

MimeFromName returns a best-effort IANA media type derived from the extension of name. Multi-segment extensions like ".tar.gz" map to MimeGZIP; unknown extensions and names without an extension fall back to MimeOctetStream.

Parameters:

  • `name`: the filename whose extension drives the lookup.

Returns:

The matching IANA media type.

Example:

sysx.MimeFromName("report.csv")  // "text/csv; charset=utf-8"
sysx.MimeFromName("dump.tar.gz") // "application/gzip"
sysx.MimeFromName("blob")        // "application/octet-stream"

func Move added in v0.1.11

func Move(src, dst string) error

Move renames (moves) src to dst. If dst already exists, it is overwritten.

Move is more robust than os.Rename: it attempts an atomic rename first, but if that fails because src and dst are on different logical devices (cross-device link error), it falls back to a manual copy-and-delete strategy.

Parameters:

  • `src`: the source file or directory path.
  • `dst`: the destination path.

Returns:

An error if the move or fallback copy fails; nil on success.

Example:

if err := sysx.Move("/tmp/data.txt", "/home/user/data.txt"); err != nil {
    log.Fatal(err)
}

func MustExecutablePath

func MustExecutablePath() string

MustExecutablePath returns the path name for the executable that started the current process and panics if the lookup fails.

Returns:

A non-empty string containing the executable path.

Example:

fmt.Println(sysx.MustExecutablePath())

func MustGetenv

func MustGetenv(key string) string

MustGetenv returns the value of the environment variable named by key and panics if the variable is not set or is empty.

Use this function only when the absence of the variable is considered an unrecoverable configuration error (e.g. during application bootstrap).

Parameters:

  • `key`: the name of the environment variable.

Returns:

A non-empty string containing the value of the variable.

Example:

dsn := sysx.MustGetenv("DATABASE_URL")

func MustHomeDir

func MustHomeDir() string

MustHomeDir returns the current user's home directory and panics if the lookup fails.

Returns:

A non-empty string containing the home directory path.

Example:

fmt.Println(sysx.MustHomeDir())

func MustHostname

func MustHostname() string

MustHostname returns the host name reported by the kernel and panics if the lookup fails.

Use this function only in contexts where a hostname lookup failure is considered a programmer error or an unrecoverable condition (e.g. during program initialisation).

Returns:

A non-empty string containing the current hostname.

Example:

host := sysx.MustHostname()
fmt.Println(host)

func MustWorkingDir

func MustWorkingDir() string

MustWorkingDir returns the current working directory of the process and panics if the lookup fails.

Returns:

A non-empty string containing the current working directory path.

Example:

fmt.Println(sysx.MustWorkingDir())

func NameNoExt added in v0.1.11

func NameNoExt(path string) string

NameNoExt returns the file name without the extension.

Parameters:

  • `path`: the file system path.

Returns:

A string containing the file name without the extension.

Example:

sysx.NameNoExt("/etc/hosts")   // "hosts"
sysx.NameNoExt("/etc/archive.zip") // "archive"

func NumCPU

func NumCPU() int

NumCPU returns the number of logical CPUs usable by the current process.

The value may be less than the total number of CPUs on the machine if the process has been constrained (e.g. via cgroups or GOMAXPROCS).

Returns:

A positive int representing the number of logical CPUs.

Example:

fmt.Println("CPUs:", sysx.NumCPU())

func NumGoroutine

func NumGoroutine() int

NumGoroutine returns the number of goroutines that currently exist in the current program.

The count includes all goroutines that have been created and not yet terminated, regardless of whether they are running or blocked.

Returns:

A positive int representing the current goroutine count.

Example:

fmt.Println("goroutines:", sysx.NumGoroutine())

func OSName

func OSName() string

OSName returns the operating system name as reported by the Go runtime.

The value is identical to runtime.GOOS and is one of the platform strings defined by the Go toolchain (e.g. "linux", "darwin", "windows", "freebsd").

Returns:

A non-empty string identifying the current operating system.

Example:

fmt.Println(sysx.OSName()) // "linux"

func OSVersion

func OSVersion() string

OSVersion returns a best-effort human-readable operating system version string.

The resolution strategy differs by platform:

  • Linux: reads /etc/os-release and returns the value of PRETTY_NAME= if present; falls back to runtime.GOOS on any error.
  • Darwin: runs `sw_vers -productVersion` and returns its trimmed stdout; falls back to runtime.GOOS on any error.
  • Windows: returns a string composed of runtime.GOOS and runtime.GOARCH.
  • Other: returns runtime.GOOS.

The function is not guaranteed to return a version number on every platform and environment. Callers that require precise version parsing should use platform-specific APIs.

Returns:

A non-empty string describing the operating system version or name.

Example:

fmt.Println(sysx.OSVersion()) // e.g. "Ubuntu 22.04.3 LTS"

func OpenFile added in v0.1.11

func OpenFile(path string, flags FileOpenFlags, perm os.FileMode) (*os.File, error)

OpenFile opens the named file with the specified flags (e.g. CWA, RO, RW) and permission bits (e.g. 0644).

By using the FileOpenFlags type, this function provides IDE auto-completion and type safety, guiding developers to use the predefined convenience constants.

Parameters:

  • `path`: the file system path to open.
  • `flags`: the type-safe FileOpenFlags combination.
  • `perm`: the os.FileMode to apply if the file is created.

Returns:

(*os.File, error): a handle to the open file and nil on success, or
nil and a non-nil error if the file cannot be opened.

Example:

// IDEs will suggest CWA, CWT, RO, etc. when typing the second argument.
f, err := sysx.OpenFile("app.log", sysx.CWA, 0644)
if err != nil {
    log.Fatal(err)
}
defer f.Close()

func PID

func PID() int

PID returns the process identifier of the current process.

It delegates directly to os.Getpid and always returns a positive integer on well-behaved platforms.

Returns:

An int representing the current process identifier.

Example:

fmt.Println("PID:", sysx.PID())

func PPID

func PPID() int

PPID returns the process identifier of the parent of the current process.

It delegates directly to os.Getppid. On Windows, this always returns 0.

Returns:

An int representing the parent process identifier.

Example:

fmt.Println("PPID:", sysx.PPID())

func ParseHostPort

func ParseHostPort(addr string) (host string, port int, err error)

ParseHostPort splits a network address of the form "host:port" into its constituent parts, returning the host string and port integer.

The function delegates to net.SplitHostPort and additionally parses the port string to an integer. IPv6 addresses must be enclosed in square brackets (e.g. "[::1]:8080").

Parameters:

  • `addr`: the "host:port" address string to parse.

Returns:

(host string, port int, err error): the host name, port number, and nil
on success, or empty values and a non-nil error when the address is
malformed.

Example:

host, port, err := sysx.ParseHostPort("localhost:8080")
// host == "localhost", port == 8080, err == nil

host, port, err = sysx.ParseHostPort("[::1]:443")
// host == "::1", port == 443, err == nil

func PathNoExt added in v0.1.11

func PathNoExt(path string) string

PathNoExt returns the path without the extension.

Parameters:

  • `path`: the file system path.

Returns:

A string containing the path without the extension.

Example:

sysx.PathNoExt("/etc/hosts")   // "/etc/hosts"
sysx.PathNoExt("/etc/archive.zip") // "/etc/archive"

func PingHost

func PingHost(host string) error

PingHost checks whether the given host is reachable by attempting a TCP connection to port 80 with a 5-second timeout.

This is a connectivity probe suitable for production backend services. It does not send ICMP packets (which require elevated privileges on most systems); instead it verifies TCP-layer reachability to the standard HTTP port.

For probing arbitrary ports, use CheckTCPConn.

Parameters:

  • `host`: the host name or IP address to probe.

Returns:

An error if the host cannot be reached via TCP port 80 within 5 seconds;
nil when the connection succeeds.

Example:

if err := sysx.PingHost("google.com"); err != nil {
    fmt.Println("host unreachable:", err)
}

func ProcessExists

func ProcessExists(pid int) bool

ProcessExists reports whether a process with the given PID is currently running.

On Unix-like systems the check is performed by sending signal 0 to the process via syscall.Kill. A nil error indicates the process exists and the caller has permission to signal it. A syscall.EPERM error also indicates the process exists (but the caller lacks permission to signal it).

On Windows, os.FindProcess never returns an error for a non-existent PID; the function therefore returns true for any positive PID on Windows. This is a known platform limitation.

Parameters:

  • `pid`: the process identifier to check.

Returns:

A boolean value:
 - true  when the process appears to be running;
 - false when the PID is invalid (≤ 0) or the process does not exist.

Example:

if sysx.ProcessExists(os.Getpid()) {
    fmt.Println("current process is running")
}

func ReadBytes added in v0.1.11

func ReadBytes(path string) ([]byte, error)

ReadBytes reads the entire contents of the file at path and returns them as a byte slice.

Parameters:

  • `path`: the file system path to read.

Returns:

([]byte, error): the file contents and nil on success, or nil and a
non-nil error if the file does not exist or cannot be read.

Example:

data, err := sysx.ReadBytes("/etc/hosts")
if err != nil {
    log.Fatal(err)
}
fmt.Printf("%d bytes read\n", len(data))

func ReadLines

func ReadLines(path string) ([]string, error)

ReadLines reads the file at path and returns its contents as a slice of strings, one element per line. Line endings ("\n" and "\r\n") are stripped from each element. An empty file returns a non-nil empty slice.

The file is read with a buffered scanner, making it efficient even for large files as long as no single line exceeds bufio.MaxScanTokenSize (64 KiB by default).

Parameters:

  • `path`: the file system path to read.

Returns:

([]string, error): lines of the file and nil on success, or a partial
result and a non-nil error on failure.

Example:

lines, err := sysx.ReadLines("/var/log/app.log")
for _, l := range lines {
    fmt.Println(l)
}

func ReadString added in v0.1.11

func ReadString(path string) (string, error)

ReadString reads the entire contents of the file at path and returns them as a string.

Parameters:

  • `path`: the file system path to read.

Returns:

(string, error): the file contents and nil on success, or an empty string
and a non-nil error if the file does not exist or cannot be read.

Example:

content, err := sysx.ReadString("/etc/hostname")
if err != nil {
    log.Fatal(err)
}
fmt.Println(strings.TrimSpace(content))

func RemoveDir

func RemoveDir(path string) error

RemoveDir removes the directory at path together with all of its contents.

It wraps os.RemoveAll. Calling RemoveDir on a path that does not exist is not an error; the function returns nil in that case.

Parameters:

  • `path`: the directory path to remove.

Returns:

An error if the directory could not be removed; nil on success or if the
path does not exist.

Example:

if err := sysx.RemoveDir("/tmp/app/cache"); err != nil {
    log.Fatal(err)
}

func RemoveDirIfExist added in v0.1.11

func RemoveDirIfExist(path string) error

RemoveDirIfExist removes the directory at path if it exists.

Parameters:

  • `path`: the directory path to remove.

Returns:

An error if the directory could not be removed; nil on success or if the
directory does not exist.

Example:

if err := sysx.RemoveDirIfExist("/tmp/cache"); err != nil {
    log.Fatal(err)
}

func RemoveFileIfExist added in v0.1.11

func RemoveFileIfExist(path string) error

RemoveFileIfExist removes the file at path if it exists.

Parameters:

  • `path`: the file path to remove.

Returns:

An error if the file could not be removed; nil on success or if the
file does not exist.

Example:

if err := sysx.RemoveFileIfExist("/tmp/app/cache/file.txt"); err != nil {
    log.Fatal(err)
}

func SafeRemove added in v0.1.11

func SafeRemove(path string)

SafeRemove removes the file at path. If the file does not exist, it does not return an error.

Parameters:

  • `path`: the file path to remove.

Example:

func main() {
	// Remove a file quietly
	sysx.SafeRemove("/tmp/app/cache/file.txt")
}

func SafeRemoveAll added in v0.1.11

func SafeRemoveAll(path string)

SafeRemoveAll removes the directory at path together with all of its contents.

It wraps os.RemoveAll. Calling SafeRemoveAll on a path that does not exist is not an error; the function returns nil in that case.

Parameters:

  • `path`: the directory path to remove.

Returns:

An error if the directory could not be removed; nil on success or if the
path does not exist.

Example:

func main() {
	// Remove a directory quietly
	sysx.SafeRemoveAll("/tmp/app/cache")
}

func Setenv

func Setenv(key string, value any) error

Setenv sets the environment variable named by key to value.

It delegates directly to os.Setenv and propagates any error.

Parameters:

  • `key`: the name of the environment variable to set.
  • `value`: the value to assign.

Returns:

An error if the variable could not be set, or nil on success.

Example:

if err := sysx.Setenv("LOG_LEVEL", "debug"); err != nil {
    log.Fatal(err)
}

func SplitPath

func SplitPath(path string) (dir, file string)

SplitPath splits path immediately following the final separator, separating it into a directory and file name component. If there is no separator in path, SplitPath returns an empty dir and file set to path. The returned values have the property that path = dir + file.

It wraps filepath.Split and is safe for concurrent use.

Parameters:

  • `path`: the file system path to split.

Returns:

(dir, file string): the directory (including trailing separator) and the
base file name.

Example:

dir, file := sysx.SplitPath("/usr/local/bin/git")
// dir  = "/usr/local/bin/"
// file = "git"

func StreamLines

func StreamLines(path string, handler func(string) error) error

StreamLines opens the file at path and calls handler for each line in order. Processing stops immediately and the handler's error is returned when handler returns a non-nil value. A bufio.Scanner error encountered during reading is also returned. Line endings are stripped before handler is invoked.

StreamLines is designed for memory-efficient processing of large files: only one line is held in memory at a time.

Parameters:

  • `path`: the file system path to read.
  • `handler`: the function called for each line; return a non-nil error to stop.

Returns:

An error if the file could not be opened, the scanner failed, or handler
returned a non-nil error; nil on success.

Example:

count := 0
err := sysx.StreamLines("/var/log/access.log", func(line string) error {
    if strings.Contains(line, "ERROR") {
        count++
    }
    return nil
})

func SystemInfo

func SystemInfo() map[string]string

SystemInfo returns a map of key/value strings summarising the most commonly needed runtime and operating system attributes of the current process.

The map always contains the following keys:

  • "os" – operating system name (runtime.GOOS)
  • "arch" – CPU architecture (runtime.GOARCH)
  • "hostname" – machine hostname; empty string on lookup failure
  • "pid" – current process identifier as a decimal string
  • "go_version" – Go runtime version (e.g. "go1.24.0")
  • "executable" – path of the current executable; empty on failure
  • "num_cpu" – number of logical CPUs as a decimal string

Returns:

A non-nil map[string]string containing the system information entries.

Example:

info := sysx.SystemInfo()
for k, v := range info {
    fmt.Printf("%s = %s\n", k, v)
}

func Tail added in v0.1.11

func Tail(path string, n int) ([]string, error)

Tail reads the last n lines of the file at path and returns them as a slice of strings with line endings stripped. If the file has fewer than n lines, all lines are returned.

The function is memory-efficient: it seeks towards the end of the file and reads only the necessary trailing portion.

Parameters:

  • `path`: the file system path to read.
  • `n`: the maximum number of lines to return.

Returns:

([]string, error): up to n lines and nil on success, or nil and a non-nil
error if the file cannot be opened or read.

Example:

logs, err := sysx.Tail("/var/log/app.log", 20)
for _, line := range logs {
    fmt.Println(line)
}

func TempDir

func TempDir() string

TempDir returns the default directory to use for temporary files.

It delegates directly to os.TempDir. On Unix, it returns $TMPDIR if set, else /tmp. On Windows it returns %TMP%, %TEMP%, or %USERPROFILE%.

Returns:

A non-empty string containing the temporary directory path.

Example:

fmt.Println(sysx.TempDir()) // "/tmp"

func Touch added in v0.1.11

func Touch(path string) error

Touch creates the file at path if it does not exist, or updates its access and modification times to the current time if it does.

Parameters:

  • `path`: the file system path to touch.

Returns:

An error if the file could not be created or its times updated; nil on success.

Example:

if err := sysx.Touch("/tmp/marker.lock"); err != nil {
    log.Fatal(err)
}

func TruncateFile

func TruncateFile(path string, size int64) error

TruncateFile truncates or extends the file at path to the given size in bytes. If size is greater than the current file size, the file is extended with zero bytes. If the file does not exist, an error is returned.

Parameters:

  • `path`: the file system path to truncate.
  • `size`: the desired file size in bytes; must be non-negative.

Returns:

An error if the file does not exist or the truncation fails; nil on success.

Example:

if err := sysx.TruncateFile("/tmp/output.bin", 0); err != nil {
    log.Fatal(err)
}

func UID

func UID() int

UID returns the numeric user identifier of the calling process.

On Windows, it always returns -1 (os.Getuid is unsupported there).

Returns:

An int representing the current user identifier.

Example:

fmt.Println("UID:", sysx.UID())

func Unsetenv

func Unsetenv(key string) error

Unsetenv removes the environment variable named by key from the process environment.

It delegates directly to os.Unsetenv and propagates any error.

Parameters:

  • `key`: the name of the environment variable to remove.

Returns:

An error if the variable could not be removed, or nil on success.

Example:

if err := sysx.Unsetenv("TEMP_TOKEN"); err != nil {
    log.Fatal(err)
}

func UserInfo

func UserInfo() string

UserInfo returns a formatted string containing the numeric user and group identifiers of the current process.

The string has the form "uid=X gid=Y", where X and Y are the values returned by UID() and GID() respectively.

Returns:

A non-empty string of the form "uid=<uid> gid=<gid>".

Example:

fmt.Println(sysx.UserInfo()) // "uid=1000 gid=1000"

func WorkingDir

func WorkingDir() (string, error)

WorkingDir returns the current working directory of the process.

It delegates directly to os.Getwd.

Returns:

(string, error): the working directory path and nil on success, or an
empty string and a non-nil error on failure.

Example:

wd, err := sysx.WorkingDir()
if err != nil {
    log.Fatal(err)
}
fmt.Println(wd)

func WriteBytes added in v0.1.11

func WriteBytes(path string, data []byte) error

WriteBytes writes data to the file at path, creating the file if it does not exist or truncating it if it does. The file is created with permission 0644.

Parameters:

  • `path`: the destination file path.
  • `data`: the bytes to write.

Returns:

An error if the file could not be created or written; nil on success.

Example:

if err := sysx.WriteBytes("/tmp/output.bin", payload); err != nil {
    log.Fatal(err)
}

func WriteBytesWithLocked added in v0.1.11

func WriteBytesWithLocked(path string, data []byte) error

WriteBytesWithLocked writes data to path using a per-path in-process mutex, ensuring that concurrent calls with the same path value are serialized. The file is created with permission 0644 if it does not exist, or truncated if it does.

Note: this provides in-process synchronization only. For cross-process safety, use AtomicWriteFile or a platform-specific file locking mechanism.

Parameters:

  • `path`: the destination file path.
  • `data`: the bytes to write.

Returns:

An error if the write failed; nil on success.

Example:

// Safe to call concurrently from multiple goroutines targeting the same path.
go sysx.WriteBytesWithLocked("/tmp/shared.json", data1)
go sysx.WriteBytesWithLocked("/tmp/shared.json", data2)

func WriteLines

func WriteLines(path string, lines []string) error

WriteLines writes each element of lines to the file at path as a separate line terminated by "\n", creating or truncating the file. A buffered writer is used for efficiency.

Parameters:

  • `path`: the destination file path.
  • `lines`: the slice of strings to write.

Returns:

An error if the file could not be created or written; nil on success.

Example:

if err := sysx.WriteLines("/tmp/list.txt", []string{"alpha", "beta", "gamma"}); err != nil {
    log.Fatal(err)
}

func WriteString added in v0.1.11

func WriteString(path string, content string) error

WriteString writes content to the file at path, creating it if it does not exist or truncating it if it does. The file is created with permission 0644.

Parameters:

  • `path`: the destination file path.
  • `content`: the string to write.

Returns:

An error if the file could not be created or written; nil on success.

Example:

if err := sysx.WriteString("/tmp/hello.txt", "hello world\n"); err != nil {
    log.Fatal(err)
}

Types

type Command

type Command struct {
	// contains filtered or unexported fields
}

Command holds the fully resolved configuration for a single external process. Use NewCommand to create a Command, then chain With* methods to configure it before calling Execute, Run, or Output.

Command is not safe for concurrent mutation; build a new value per goroutine when the same logical command must be launched from multiple goroutines.

func NewCommand

func NewCommand(name string) *Command

NewCommand creates a new Command for the program identified by name.

name may be an absolute path, a relative path, or a bare program name that is resolved through the process $PATH. No validation is performed until Execute, Run, or Output is called.

Parameters:

  • `name`: the program name or path to execute.

Returns:

A pointer to a new Command ready for configuration.

Example:

result := sysx.NewCommand("git").

WithArgs("rev-parse", "HEAD").
WithDir("/path/to/repo").
Execute()

if result.IsSuccess() {
   fmt.Println(strings.TrimSpace(result.Stdout()))
}

func (*Command) Args

func (c *Command) Args() []string

Args returns the positional arguments configured on the Command.

Returns:

A slice of strings containing the command-line arguments.

Example:

cmd := sysx.NewCommand("git").WithArgs("log", "--oneline")
fmt.Println(cmd.Args()) // ["log", "--oneline"]

func (*Command) Dir

func (c *Command) Dir() string

Dir returns the working directory configured on the Command. An empty string means the child process inherits the caller's directory.

Returns:

A string containing the working directory path, or empty if not set.

func (*Command) Env

func (c *Command) Env() []string

Env returns the extra environment variable bindings ("KEY=VALUE") that will be merged on top of the calling process environment when the command is executed.

Returns:

A slice of "KEY=VALUE" strings; nil if no extra bindings were added.

func (*Command) Execute

func (c *Command) Execute() *CommandResult

Execute runs the command and returns a CommandResult containing captured stdout, stderr, exit code, wall-clock duration, and any error.

If WithStdout or WithStderr were provided, the corresponding fields in CommandResult are empty because the data was streamed to those writers.

Execute is safe to call multiple times; each call spawns a new process.

Returns:

A non-nil *CommandResult describing the outcome of the command.

Example:

res := sysx.NewCommand("bash").

WithArgs("-c", "echo hello").
WithTimeout(5 * time.Second).
WithEnv("APP_ENV=prod").
WithDir("/tmp").
Execute()

fmt.Printf("exit=%d stdout=%q duration=%v\n", res.ExitCode(), res.Stdout(), res.Duration())

func (*Command) Name

func (c *Command) Name() string

Name returns the program name or path configured on the Command.

Returns:

A string containing the program name or path.

Example:

cmd := sysx.NewCommand("git")
fmt.Println(cmd.Name()) // "git"

func (*Command) Output

func (c *Command) Output() (string, error)

Output runs the command and returns the combined stdout+stderr as a string along with any error.

Returns:

(string, error): the combined output and nil on success, or combined partial output and a non-nil error on failure.

func (*Command) Run

func (c *Command) Run() error

Run runs the command, discarding all output, and returns only the error. It is a convenience method equivalent to calling Execute and inspecting the Err() result.

Returns:

An error if the command could not be started or exited non-zero; nil on success.

func (*Command) Timeout

func (c *Command) Timeout() time.Duration

Timeout returns the maximum execution duration configured on the Command. A zero duration means no timeout is applied.

Returns:

A time.Duration; zero if no timeout was set.

func (*Command) WithArgs

func (c *Command) WithArgs(args ...string) *Command

WithArgs sets the positional arguments passed to the program. Calling WithArgs replaces any previously set arguments.

Parameters:

  • `args`: the command-line arguments.

Returns:

The receiver, enabling method chaining.

func (*Command) WithContext

func (c *Command) WithContext(ctx context.Context) *Command

WithContext attaches an existing context to the command, enabling external cancellation and deadline propagation. If both WithContext and WithTimeout are used, the more restrictive deadline wins.

Parameters:

  • `ctx`: the context to attach.

Returns:

The receiver, enabling method chaining.

func (*Command) WithDir

func (c *Command) WithDir(dir string) *Command

WithDir sets the working directory for the command. An empty string leaves the working directory unchanged (the child inherits the calling process's directory).

Parameters:

  • `dir`: the working directory path.

Returns:

The receiver, enabling method chaining.

func (*Command) WithEnv

func (c *Command) WithEnv(env ...string) *Command

WithEnv appends one or more environment variable bindings in "KEY=VALUE" form to the command's environment. Bindings are merged on top of the calling process environment; later values for the same key shadow earlier ones. Calling WithEnv multiple times accumulates bindings.

Parameters:

  • `env`: one or more "KEY=VALUE" environment variable bindings.

Returns:

The receiver, enabling method chaining.

Example:

sysx.NewCommand("go").

WithArgs("build", "./...").
WithEnv("GOOS=linux", "GOARCH=amd64").
Execute()

func (*Command) WithEnvCast

func (c *Command) WithEnvCast(key, value any) *Command

WithEnvCast sets a single environment variable binding. This is a convenience method that is equivalent to calling WithEnv(fmt.Sprintf("%s=%s", key, value)).

Parameters:

  • `key`: the environment variable key.
  • `value`: the environment variable value.

Returns:

The receiver, enabling method chaining.

func (*Command) WithEnvf

func (c *Command) WithEnvf(format string, args ...any) *Command

WithEnvf appends an environment variable binding created from a format string. This is a convenience method that is equivalent to calling WithEnv(fmt.Sprintf(format, args...)).

Parameters:

  • `format`: the format string.
  • `args`: the arguments to format.

Returns:

The receiver, enabling method chaining.

func (*Command) WithStderr

func (c *Command) WithStderr(w io.Writer) *Command

WithStderr sets the writer to which the command's standard error is forwarded in real time. When a custom writer is provided, CommandResult.Stderr() will be empty because data is not buffered internally.

Parameters:

  • `w`: the io.Writer to receive stderr.

Returns:

The receiver, enabling method chaining.

func (*Command) WithStdin

func (c *Command) WithStdin(r io.Reader) *Command

WithStdin sets the reader that supplies the command's standard input.

Parameters:

  • `r`: the io.Reader to use as stdin.

Returns:

The receiver, enabling method chaining.

func (*Command) WithStdout

func (c *Command) WithStdout(w io.Writer) *Command

WithStdout sets the writer to which the command's standard output is forwarded in real time. When a custom writer is provided, CommandResult.Stdout() will be empty because data is not buffered internally.

Parameters:

  • `w`: the io.Writer to receive stdout.

Returns:

The receiver, enabling method chaining.

func (*Command) WithTimeout

func (c *Command) WithTimeout(d time.Duration) *Command

WithTimeout sets a maximum duration for the command. If the command does not finish within the deadline the process is killed and Execute returns a CommandResult whose Err() wraps context.DeadlineExceeded.

A zero or negative duration means no timeout is applied.

Parameters:

  • `d`: the maximum duration to wait.

Returns:

The receiver, enabling method chaining.

type CommandResult

type CommandResult struct {
	// contains filtered or unexported fields
}

CommandResult holds the structured outcome of a completed command execution. All fields are always populated; zero values indicate the field was not applicable (e.g. ExitCode() == 0 means success, Duration() == 0 on immediate failure).

Use the accessor methods to read individual result fields.

func RunCommand

func RunCommand(name string, args ...string) *CommandResult

RunCommand creates a Command for name, runs it with the provided args, and returns the structured CommandResult.

It is the single-call equivalent of:

NewCommand(name).WithArgs(args...).Execute()

Parameters:

  • `name`: the program name or path.
  • `args`: optional arguments.

Returns:

A non-nil *CommandResult.

Example:

res := sysx.RunCommand("git", "status")

if !res.IsSuccess() {
   log.Printf("exit %d: %s", res.ExitCode(), res.Stderr())
}

func (*CommandResult) Combined

func (r *CommandResult) Combined() string

Combined returns the concatenation of Stdout followed by Stderr.

Returns:

A string containing the combined output of the command.

func (*CommandResult) Duration

func (r *CommandResult) Duration() time.Duration

Duration returns the wall-clock time spent waiting for the command to complete.

Returns:

A time.Duration representing the execution time.

func (*CommandResult) Err

func (r *CommandResult) Err() error

Err returns the error from command execution. A non-nil value indicates the command could not be started or exited non-zero.

Returns:

An error describing the failure, or nil on success.

func (*CommandResult) ExitCode

func (r *CommandResult) ExitCode() int

ExitCode returns the process exit code; 0 indicates success. -1 indicates that the exit code could not be determined (e.g. the process was killed by a signal or a context was cancelled).

Returns:

An int representing the process exit code.

func (*CommandResult) IsSuccess

func (r *CommandResult) IsSuccess() bool

Success reports whether the command completed without error.

Returns:

true when Err() is nil; false otherwise.

func (*CommandResult) Stderr

func (r *CommandResult) Stderr() string

Stderr returns the captured standard error of the command. Returns an empty string when a custom io.Writer was provided via WithStderr.

Returns:

A string containing the captured stderr of the command.

func (*CommandResult) Stdout

func (r *CommandResult) Stdout() string

Stdout returns the captured standard output of the command. Returns an empty string when a custom io.Writer was provided via WithStdout.

Returns:

A string containing the captured stdout of the command.

type FileOpenFlags added in v0.1.11

type FileOpenFlags int

FileOpenFlags is a convenience type for file open flags that provides a type-safe way to specify common flag combinations. When a function accepts this type, IDEs will suggest the predefined constants (e.g. CWA, RO, RW).

const (
	// os.O_CREATE | os.O_WRONLY | os.O_APPEND
	// Flag for opening a file for writing with append mode and creating it if it doesn't exist.
	CWA FileOpenFlags = FileOpenFlags(os.O_CREATE | os.O_WRONLY | os.O_APPEND)

	// os.O_CREATE | os.O_WRONLY | os.O_TRUNC
	// Flag for opening a file for writing with truncate mode and creating it if it doesn't exist.
	CWT FileOpenFlags = FileOpenFlags(os.O_CREATE | os.O_WRONLY | os.O_TRUNC)

	// os.O_CREATE | os.O_WRONLY
	// Flag for opening a file for writing and creating it if it doesn't exist.
	CW FileOpenFlags = FileOpenFlags(os.O_CREATE | os.O_WRONLY)

	// os.O_APPEND | os.O_WRONLY
	// Flag for opening a file for writing with append mode.
	AW FileOpenFlags = FileOpenFlags(os.O_APPEND | os.O_WRONLY)

	// os.O_TRUNC | os.O_WRONLY
	// Flag for opening a file for writing with truncate mode.
	TW FileOpenFlags = FileOpenFlags(os.O_TRUNC | os.O_WRONLY)

	// os.O_RDONLY
	// Flag for opening a file for reading only.
	RO FileOpenFlags = FileOpenFlags(os.O_RDONLY)

	// os.O_RDWR
	// Flag for opening a file for reading and writing.
	RW FileOpenFlags = FileOpenFlags(os.O_RDWR)

	// os.O_RDWR | os.O_CREATE
	// Flag for opening a file for reading and writing and creating it if it doesn't exist.
	CRW FileOpenFlags = FileOpenFlags(os.O_RDWR | os.O_CREATE)

	// os.O_RDWR | os.O_CREATE | os.O_TRUNC
	// Flag for opening a file for reading and writing with truncate mode and creating it if it doesn't exist.
	CRWT FileOpenFlags = FileOpenFlags(os.O_RDWR | os.O_CREATE | os.O_TRUNC)

	// os.O_RDWR | os.O_APPEND
	// Flag for opening a file for reading and writing with append mode.
	ARW FileOpenFlags = FileOpenFlags(os.O_RDWR | os.O_APPEND)

	// os.O_RDWR | os.O_APPEND | os.O_TRUNC
	// Flag for opening a file for reading and writing with append and truncate mode and creating it if it doesn't exist.
	TARW FileOpenFlags = FileOpenFlags(os.O_RDWR | os.O_APPEND | os.O_TRUNC)

	// os.O_RDWR | os.O_TRUNC
	// Flag for opening a file for reading and writing with truncate mode.
	TRW FileOpenFlags = FileOpenFlags(os.O_RDWR | os.O_TRUNC)

	// os.O_RDWR | os.O_CREATE | os.O_APPEND
	// Flag for opening a file for reading and writing with append mode and creating it if it doesn't exist.
	CRWA FileOpenFlags = FileOpenFlags(os.O_RDWR | os.O_CREATE | os.O_APPEND)
)

func (FileOpenFlags) IsValid added in v0.1.11

func (f FileOpenFlags) IsValid() bool

IsValid reports whether the flag combination is one of the predefined constants.

Parameters:

  • `f`: the FileOpenFlags combination.

Returns:

bool: true if the flag combination is valid, false otherwise.

Example:

fmt.Println(sysx.CWA.IsValid()) // true
fmt.Println(sysx.FileOpenFlags(999).IsValid()) // false

func (FileOpenFlags) String added in v0.1.11

func (f FileOpenFlags) String() string

String returns a human-readable label for the flag combination.

Parameters:

  • `f`: the FileOpenFlags combination.

Returns:

A string containing the label.

Example:

fmt.Println(sysx.CWA.String()) // "CWA (CREATE|WRONLY|APPEND)"

type MemBlob added in v0.1.24

type MemBlob struct {
	// contains filtered or unexported fields
}

MemBlob is an in-memory ReadSeekCloser backed by a byte slice. It is the cheapest backing implementation and is ideal for small payloads that comfortably fit in process memory. Close is a no-op; the underlying slice is released when the MemBlob becomes unreachable.

func NewMemBlob added in v0.1.24

func NewMemBlob(data []byte) *MemBlob

NewMemBlob wraps the supplied byte slice in a MemBlob backing.

The slice is retained, not copied; callers must not mutate it for the lifetime of the returned value. The Reader is positioned at offset 0.

Parameters:

  • `data`: the payload to expose as a seekable, closeable stream.

Returns:

A pointer to a MemBlob ready for consumption by Resource.

Example:

blob := sysx.NewMemBlob([]byte("hello"))
defer blob.Close()

func (*MemBlob) Bytes added in v0.1.24

func (m *MemBlob) Bytes() []byte

Bytes returns the underlying byte slice. The returned slice is the same one passed to NewMemBlob; callers must not mutate it.

Returns:

The payload bytes.

func (*MemBlob) Close added in v0.1.24

func (m *MemBlob) Close() error

Close implements io.Closer. It is a no-op; the underlying byte slice is released by the garbage collector when the MemBlob becomes unreachable.

Returns:

nil.

func (*MemBlob) Len added in v0.1.24

func (m *MemBlob) Len() int64

Len returns the total number of bytes in the blob.

Returns:

The blob length in bytes.

func (*MemBlob) Read added in v0.1.24

func (m *MemBlob) Read(p []byte) (int, error)

Read implements io.Reader by delegating to the embedded *bytes.Reader.

func (*MemBlob) Seek added in v0.1.24

func (m *MemBlob) Seek(offset int64, whence int) (int64, error)

Seek implements io.Seeker by delegating to the embedded *bytes.Reader.

type ReadSeekCloser added in v0.1.24

type ReadSeekCloser interface {
	io.Reader
	io.Seeker
	io.Closer
}

ReadSeekCloser is the I/O contract every Resource payload must satisfy. It composes the three standard library interfaces required by downstream consumers (HTTP responses, S3 uploader, archive writers, Telegram bots, …) without binding the producer to a concrete type such as *os.File.

type Resource added in v0.1.24

type Resource struct {
	// contains filtered or unexported fields
}

Resource is the storage-agnostic envelope returned by every data-exporting workflow (reports, dumps, archives, attachments, backups, media, rendered documents). All fields are unexported; use the constructor NewResource and the chainable With* methods to populate the instance, and the accessor methods to read it.

A Resource owns its underlying ReadSeekCloser; consumers must invoke Close exactly once to release file handles, delete temporary files, or free buffers as appropriate to the backing implementation.

func NewResource added in v0.1.24

func NewResource() *Resource

NewResource creates an empty Resource ready to be configured through the chainable With* setters and one of the From* loaders.

The returned Resource has Size() == 0, an empty Name and ContentType, nil Content, and DefaultSpillThreshold as its streaming threshold.

Returns:

A pointer to a freshly initialized Resource.

Example:

res := sysx.NewResource().
    WithName("user-report.csv").
    WithContentType(sysx.MimeCSV).
    FromBytes(payload)
defer res.Close()

func (*Resource) ActualPath added in v0.1.24

func (r *Resource) ActualPath() string

ActualPath returns the on-disk path of the file backing this Resource.

The returned path is non-empty only when the Resource is backed by a concrete file:

  • [FromTempFile] — always returns the actual temp file path.
  • [FromReader] with spill — returns the spill temp file path once the in-memory threshold has been exceeded.
  • [FromReader] without spill / [FromBytes] / [FromString] — returns "" because the payload is held entirely in memory.
  • [FromFile] — returns the path of the adopted file.

The returned string is valid as long as the Resource has not been closed. Callers must not use the path after Close, as the underlying file may have been deleted.

Returns:

The absolute on-disk path, or an empty string for memory-backed Resources.

func (*Resource) Close added in v0.1.24

func (r *Resource) Close() error

Close releases the underlying ReadSeekCloser, tolerating a nil Resource or a nil Content. Calling Close more than once is safe; the underlying backings are required to be idempotent.

Returns:

Any error returned by the underlying Close, or nil when there is nothing to close.

func (*Resource) Content added in v0.1.24

func (r *Resource) Content() ReadSeekCloser

Content returns the owned ReadSeekCloser stream backing the Resource. Callers must invoke Close exactly once when done reading, either directly on the returned value or through Resource.Close.

Returns:

The configured ReadSeekCloser, or nil if no content has been loaded.

func (*Resource) ContentType added in v0.1.24

func (r *Resource) ContentType() string

ContentType returns the IANA media type configured on the Resource. An empty string means the producer could not determine one.

Returns:

The configured media type.

func (*Resource) CopyTo added in v0.1.24

func (r *Resource) CopyTo(dst io.Writer) (int64, error)

CopyTo streams the Resource payload into dst and returns the number of bytes written. The Resource is NOT closed; the caller retains ownership and must invoke Close once consumption is complete.

Parameters:

  • `dst`: the destination writer.

Returns:

The number of bytes copied and any error encountered during the copy.

func (*Resource) Drain added in v0.1.24

func (r *Resource) Drain() (int64, error)

Drain reads and discards the entire Resource payload. It is useful for integration tests, dry-run pipelines, and benchmark shaping where the caller wants to exercise the producer end-to-end without preserving the output.

Returns:

The number of bytes discarded and any error encountered during the read.

func (*Resource) FromBytes added in v0.1.24

func (r *Resource) FromBytes(data []byte) *Resource

FromBytes installs an in-memory MemBlob backing populated from data and updates Size accordingly. The slice is retained, not copied; callers must not mutate it for the lifetime of the Resource.

If ContentType has not already been set, it is inferred from Name via MimeFromName.

Parameters:

  • `data`: the payload bytes.

Returns:

The receiver, enabling method chaining.

func (*Resource) FromFile added in v0.1.24

func (r *Resource) FromFile(f *os.File) (*Resource, error)

FromFile adopts an existing on-disk file as the Resource backing. The file is rewound and stat-ed; Size and (when not yet set) Name are populated from the file's metadata. Whether the file is unlinked on Close is controlled by WithRemoveOnClose (defaults to true).

FromFile is the only Resource loader that accepts a raw *os.File; this is intentional, as sysx is the package's infrastructure layer. Callers above sysx must depend on Resource exclusively.

Parameters:

  • `f`: an open file with read permission.

Returns:

The receiver and any error encountered while stat-ing or rewinding the file.

func (*Resource) FromReader added in v0.1.24

func (r *Resource) FromReader(src io.Reader) (*Resource, error)

FromReader drains an arbitrary io.Reader into a hybrid buffer that starts in memory and spills to a temporary file once SpillThreshold bytes have been accumulated. The resulting backing satisfies ReadSeekCloser, allowing consumers (S3 multipart, archive writers, …) to seek freely even though the original producer was not seekable.

Parameters:

  • `src`: the producer stream; must not be nil.

Returns:

The receiver and any error encountered while draining or sealing the buffer.

func (*Resource) FromString added in v0.1.24

func (r *Resource) FromString(s string) *Resource

FromString is a convenience wrapper around FromBytes for textual payloads.

Parameters:

  • `s`: the payload text.

Returns:

The receiver, enabling method chaining.

func (*Resource) FromTempFile added in v0.1.24

func (r *Resource) FromTempFile(write func(io.Writer) error) (*Resource, error)

FromTempFile creates a fresh temporary file using the configured TempPattern and TempDir, invokes write with an io.Writer view of the file, and installs the result as the Resource backing. The file is rewound and stat-ed before returning, so consumers can read it immediately.

On any failure the temp file is closed and removed before returning.

Parameters:

  • `write`: producer callback that serialize the payload. The argument is intentionally io.Writer (not *os.File) so producers cannot smuggle out the underlying handle and break the abstraction.

Returns:

The receiver and any error encountered during creation, writing, stat-ing, or rewinding.

func (*Resource) Name added in v0.1.24

func (r *Resource) Name() string

Name returns the suggested filename advertised to consumers, including any extension. An empty string means the producer has not assigned one.

Returns:

The configured filename.

func (*Resource) RemoveOnClose added in v0.1.24

func (r *Resource) RemoveOnClose() bool

RemoveOnClose reports whether temp files created by FromTempFile and by the spill path of FromReader will be unlinked when the Resource is closed. Defaults to true.

Returns:

The configured auto-removal flag.

func (*Resource) Rewind added in v0.1.24

func (r *Resource) Rewind() error

Rewind seeks Content back to offset 0. It is intended for consumers that need to re-read a Resource — for example, an HTTP retry path or an upload that must recompute a checksum.

Returns:

Any error returned by Seek, or ErrNilResource when Content is nil.

func (*Resource) Size added in v0.1.24

func (r *Resource) Size() int64

Size returns the total number of bytes available from Content, or -1 if the size is not known up-front (typical for unbounded streaming backends).

Returns:

The configured payload size in bytes.

func (*Resource) SpillThreshold added in v0.1.24

func (r *Resource) SpillThreshold() int64

SpillThreshold returns the in-memory ceiling, in bytes, applied by FromReader before spilling to a temporary file. A value of 0 means the default (DefaultSpillThreshold) is used.

Returns:

The configured spill threshold in bytes.

func (*Resource) TempDir added in v0.1.24

func (r *Resource) TempDir() string

TempDir returns the parent directory used by FromTempFile and by the spill path of FromReader. An empty string means the system default temp directory (os.TempDir) is used.

Returns:

The configured parent directory.

func (*Resource) TempPattern added in v0.1.24

func (r *Resource) TempPattern() string

TempPattern returns the os.CreateTemp pattern used by FromTempFile and by FromReader's spill path. An empty string means the package default (defaultTempPattern) is used.

Returns:

The configured temp-file pattern.

func (*Resource) WithContent added in v0.1.24

func (r *Resource) WithContent(c ReadSeekCloser) *Resource

WithContent attaches a custom ReadSeekCloser to the Resource. The caller is responsible for setting Size separately when known.

Parameters:

  • `c`: the ReadSeekCloser owning the payload bytes.

Returns:

The receiver, enabling method chaining.

func (*Resource) WithContentType added in v0.1.24

func (r *Resource) WithContentType(mime string) *Resource

WithContentType sets the IANA media type explicitly, overriding any auto-detection performed by From* loaders.

Parameters:

  • `mime`: the IANA media type, e.g. sysx.MimeCSV.

Returns:

The receiver, enabling method chaining.

func (*Resource) WithName added in v0.1.24

func (r *Resource) WithName(name string) *Resource

WithName sets the suggested filename advertised to consumers. The name should include any extension that matches ContentType so consumers can derive the correct headers (e.g. Content-Disposition).

Parameters:

  • `name`: the filename to advertise.

Returns:

The receiver, enabling method chaining.

func (*Resource) WithRemoveOnClose added in v0.1.24

func (r *Resource) WithRemoveOnClose(remove bool) *Resource

WithRemoveOnClose configures whether temporary files created on behalf of this Resource (by FromTempFile and the spill path of FromReader) will be unlinked when the Resource is closed.

Parameters:

  • `remove`: true to auto-remove temp files; false to retain them.

Returns:

The receiver, enabling method chaining.

func (*Resource) WithSize added in v0.1.24

func (r *Resource) WithSize(size int64) *Resource

WithSize overrides the payload size in bytes. From* loaders set this automatically; calling WithSize is rarely necessary unless the producer already knows the total length up-front and is providing a custom Content via WithContent.

Parameters:

  • `size`: the payload size in bytes; -1 means unknown.

Returns:

The receiver, enabling method chaining.

func (*Resource) WithSpillThreshold added in v0.1.24

func (r *Resource) WithSpillThreshold(bytes int64) *Resource

WithSpillThreshold configures the in-memory ceiling, in bytes, applied by FromReader before spilling the rest of the stream to a temporary file. A non-positive value resets the threshold to DefaultSpillThreshold.

Parameters:

  • `bytes`: the in-memory ceiling.

Returns:

The receiver, enabling method chaining.

func (*Resource) WithTempDir added in v0.1.24

func (r *Resource) WithTempDir(dir string) *Resource

WithTempDir configures the parent directory used by FromTempFile and by the spill path of FromReader. An empty string falls back to os.TempDir.

Parameters:

  • `dir`: the parent directory.

Returns:

The receiver, enabling method chaining.

func (*Resource) WithTempPattern added in v0.1.24

func (r *Resource) WithTempPattern(pattern string) *Resource

WithTempPattern configures the os.CreateTemp pattern used by FromTempFile and by the spill path of FromReader. Use "*" as the random suffix placeholder, e.g. "user-report-*.csv".

Parameters:

  • `pattern`: the os.CreateTemp pattern.

Returns:

The receiver, enabling method chaining.

type SafeFileWriter

type SafeFileWriter struct {
	// contains filtered or unexported fields
}

SafeFileWriter provides concurrency-safe append and overwrite operations targeting a single file path. A single SafeFileWriter instance can be shared across goroutines; all write operations are serialize by an internal mutex.

Create a SafeFileWriter with NewSafeFileWriter and optionally adjust the file permission with WithPerm before sharing across goroutines.

func NewSafeFileWriter

func NewSafeFileWriter(path string) *SafeFileWriter

NewSafeFileWriter creates a SafeFileWriter targeting path with the default file permission of 0644.

Parameters:

  • `path`: the file path to write to.

Returns:

A pointer to a new SafeFileWriter.

Example:

w := sysx.NewSafeFileWriter("/var/log/app.log")
go w.WriteString("line from goroutine 1\n")
go w.WriteString("line from goroutine 2\n")

func (*SafeFileWriter) Overwrite

func (w *SafeFileWriter) Overwrite(data []byte) error

Overwrite replaces the entire file content with data, atomically using the temporary-file-and-rename pattern. The operation is serialized by an internal mutex.

Parameters:

  • `data`: the bytes to write.

Returns:

An error if the write or rename failed; nil on success.

func (*SafeFileWriter) Path

func (w *SafeFileWriter) Path() string

Path returns the file path targeted by this SafeFileWriter.

Returns:

A string containing the file path.

func (*SafeFileWriter) Perm

func (w *SafeFileWriter) Perm() os.FileMode

Perm returns the file permission used when creating the file.

Returns:

An os.FileMode representing the file permission.

func (*SafeFileWriter) WithPerm

func (w *SafeFileWriter) WithPerm(perm os.FileMode) *SafeFileWriter

WithPerm overrides the file permission used when creating the file. The default is 0644.

Parameters:

  • `perm`: the os.FileMode to apply on file creation.

Returns:

The receiver, enabling method chaining.

func (*SafeFileWriter) WriteBytes added in v0.1.11

func (w *SafeFileWriter) WriteBytes(data []byte) error

WriteBytes appends data to the file, creating it if it does not exist. The operation is serialized by an internal mutex, making it safe to call concurrently from multiple goroutines.

Parameters:

  • `data`: the bytes to append.

Returns:

An error if the file could not be opened or written; nil on success.

func (*SafeFileWriter) WriteString

func (w *SafeFileWriter) WriteString(s string) error

WriteString appends s to the file, creating it if it does not exist. The operation is serialized by an internal mutex.

Parameters:

  • `s`: the string to append.

Returns:

An error if the file could not be opened or written; nil on success.

type TempFile added in v0.1.24

type TempFile struct {
	// contains filtered or unexported fields
}

TempFile is a ReadSeekCloser backed by an on-disk temporary file. It is the only public type in sysx that wraps an *os.File for the purpose of satisfying the Resource contract; outside of sysx, callers must depend on Resource and ReadSeekCloser instead.

When removeOnClose is true the underlying file is unlinked on the first Close call. Subsequent Close calls are no-ops so the value remains safe to defer.

func NewTempFile added in v0.1.24

func NewTempFile() (*TempFile, error)

NewTempFile creates a new temporary file in the system default temp directory using defaultTempPattern. The returned TempFile is armed for auto-removal on Close.

Returns:

A pointer to the new TempFile and any error returned by os.CreateTemp.

Example:

tf, err := sysx.NewTempFile()
if err != nil {
    return err
}
defer tf.Close()

func NewTempFileAt added in v0.1.24

func NewTempFileAt(dir, pattern string) (*TempFile, error)

NewTempFileAt creates a new temporary file using pattern inside dir. An empty dir falls back to the system default temp directory; an empty pattern falls back to defaultTempPattern. The returned TempFile is armed for auto-removal on Close.

Parameters:

  • `dir`: the parent directory; empty for the default temp dir.
  • `pattern`: the os.CreateTemp pattern.

Returns:

A pointer to the new TempFile and any error returned by os.CreateTemp.

func NewTempFilename added in v0.1.24

func NewTempFilename(pattern string) (*TempFile, error)

NewTempFilename creates a new temporary file using the supplied os.CreateTemp pattern (e.g. "user-report-*.csv") in the default temp directory. The returned TempFile is armed for auto-removal on Close.

Parameters:

  • `pattern`: the os.CreateTemp pattern.

Returns:

A pointer to the new TempFile and any error returned by os.CreateTemp.

func (*TempFile) Close added in v0.1.24

func (t *TempFile) Close() error

Close closes the underlying file and, when armed, removes it from disk. It is safe to call multiple times; only the first invocation performs any work, so the value remains safe to defer.

Returns:

The first non-nil error encountered between closing and unlinking, or nil when both succeed (or when Close has already been called).

func (*TempFile) Closed added in v0.1.24

func (t *TempFile) Closed() bool

Closed reports whether Close has already been invoked on the TempFile. Subsequent Close calls are no-ops once Closed returns true.

Returns:

true after the first successful Close.

func (*TempFile) Path added in v0.1.24

func (t *TempFile) Path() string

Path returns the path of the temporary file as reported by the underlying *os.File.

Returns:

The on-disk path of the temp file.

func (*TempFile) Read added in v0.1.24

func (t *TempFile) Read(p []byte) (int, error)

Read implements io.Reader by delegating to the underlying *os.File.

func (*TempFile) RemoveOnClose added in v0.1.24

func (t *TempFile) RemoveOnClose() bool

RemoveOnClose reports whether the underlying file will be unlinked the next time Close is called.

Returns:

true when auto-removal is armed.

func (*TempFile) Seek added in v0.1.24

func (t *TempFile) Seek(offset int64, whence int) (int64, error)

Seek implements io.Seeker by delegating to the underlying *os.File.

func (*TempFile) Stat added in v0.1.24

func (t *TempFile) Stat() (os.FileInfo, error)

Stat returns the FileInfo describing the temporary file.

Returns:

The os.FileInfo and any error returned by the underlying Stat call.

func (*TempFile) WithRemoveOnClose added in v0.1.24

func (t *TempFile) WithRemoveOnClose(remove bool) *TempFile

WithRemoveOnClose configures whether the underlying file will be unlinked the next time Close is called. Use this to disarm auto- removal before handing the file off to another owner (for example, by renaming it into a permanent location).

Parameters:

  • `remove`: true to arm auto-removal; false to disarm it.

Returns:

The receiver, enabling method chaining.

func (*TempFile) Write added in v0.1.24

func (t *TempFile) Write(p []byte) (int, error)

Write implements io.Writer by delegating to the underlying *os.File. It is exposed so FromTempFile can hand a write-only view to producer callbacks without leaking the *os.File itself.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL