Documentation
¶
Overview ¶
Package runutil provides utilities for executing external commands and processes with support for piping data between Go native functions and UNIX commands.
This library makes it easy to execute complex sequences of executables, mixing both Go native methods like gzip.NewReader and UNIX commands in the way pipes work in bash and other shells.
Basic Command Execution ¶
The simplest way to run a command is with the Run function:
err := runutil.Run("ls", "-la")
To capture output, use RunGet:
output, err := runutil.RunGet("date")
Piping ¶
The library excels at creating complex pipelines. Use RunPipe to connect an input stream to a command and get its output:
compressed, err := runutil.RunPipe(input, "gzip", "-9")
if err != nil {
return err
}
// compressed is now a Pipe that can be read or passed to another command
The returned Pipe interface extends io.ReadCloser with additional methods for proper resource management.
Environment Management ¶
Commands can be run with custom environments using the Env type:
env := runutil.NewEnv("/home/user", "CUSTOM_VAR=value")
err := env.Run("my-command")
Error Handling ¶
Unlike standard os/exec, runutil properly propagates errors from commands. If a command in a pipeline fails, the error is returned when reading from the pipe, ensuring no errors are silently ignored.
Resource Management ¶
When using RunRead or RunPipe, always ensure the returned Pipe is closed to prevent zombie processes:
pipe, err := runutil.RunRead("long-running-command")
if err != nil {
return err
}
defer pipe.Close()
// read from pipe...
For graceful shutdown with a timeout, use CloseWait:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() err := pipe.CloseWait(ctx)
Process Inspection (Linux) ¶
On Linux, the package provides functions for process discovery and state inspection:
pids := runutil.PidOf("nginx")
state, err := runutil.PidState(uint64(pids[0]))
if err == nil && state.IsRunning() {
started, _ := state.Started()
fmt.Printf("Process started at: %v\n", started)
}
Zombie Process Cleanup ¶
The Reap function can be called periodically to clean up zombie child processes:
err := runutil.Reap()
Index ¶
- Variables
- func ArgsOf(pid int) ([]string, error)
- func PidOf(name string) (res []int)
- func Reap() error
- func Run(arg ...string) error
- func RunGet(arg ...string) ([]byte, error)
- func RunJson(obj interface{}, arg ...string) error
- func RunWrite(r io.Reader, arg ...string) error
- func Sh(cmd string) error
- func ShQuote(s string) string
- type Env
- func (e Env) Contains(k string) bool
- func (e Env) Dedup() Env
- func (e Env) Get(k string) string
- func (e Env) Join(others ...Env) Env
- func (e Env) Run(arg ...string) error
- func (e Env) RunGet(arg ...string) ([]byte, error)
- func (e Env) RunJson(obj interface{}, arg ...string) error
- func (e Env) RunPipe(r io.Reader, arg ...string) (Pipe, error)
- func (e Env) RunRead(arg ...string) (Pipe, error)
- func (e Env) RunWrite(r io.Reader, arg ...string) error
- func (e *Env) Set(k, v string)
- func (e *Env) Unset(k string)
- type LinuxProcState
- type Pipe
- type ProcState
Constants ¶
This section is empty.
Variables ¶
var ( // ErrCommandMissing is returned when a Run function is called with no arguments. ErrCommandMissing = errors.New("command is missing") // ErrNotSupported is returned when a platform-specific operation is called // on an unsupported platform (e.g., PidState on non-Linux systems). ErrNotSupported = errors.New("operation not supported on this platform") )
Package-level errors returned by runutil functions.
Functions ¶
func ArgsOf ¶ added in v0.2.5
ArgsOf returns the command-line arguments of the process with the given PID. It reads /proc/<pid>/cmdline and splits it on null bytes to get individual arguments. The first element is typically the program name. Returns an error if the process does not exist or cannot be read. This function is only available on Linux.
func PidOf ¶ added in v0.1.0
PidOf finds all process IDs whose executable name matches the given name. It searches /proc for all running processes and checks both /proc/<pid>/cmdline and /proc/<pid>/exe to find matches. Returns an empty slice if no matches are found. This function is only available on Linux.
func Reap ¶ added in v0.0.4
func Reap() error
Reap cleans up zombie child processes by repeatedly calling wait4 with WNOHANG until no more terminated children remain. This is useful when running as PID 1 (init) or when spawning many short-lived child processes that may become zombies. On Unix systems, zombie processes consume a process table entry until their parent calls wait; this function releases those entries.
func Run ¶
Run executes a command and waits for it to complete. The command's stdout and stderr are forwarded to os.Stdout and os.Stderr. The first argument is the command name, and subsequent arguments are passed to the command. Returns ErrCommandMissing if no arguments are provided.
func RunGet ¶
RunGet executes a command and returns its stdout as a byte slice. The function waits for the command to complete before returning. This is useful for capturing command output for further processing. Returns ErrCommandMissing if no command arguments are provided.
func RunJson ¶
RunJson executes a command and decodes its JSON stdout into the provided object. The obj parameter should be a pointer to the type you want to decode into. This is useful for commands that output structured JSON data. Returns ErrCommandMissing if no command arguments are provided.
func RunWrite ¶
RunWrite executes a command with the provided Reader as stdin. The command's stdout and stderr are forwarded to os.Stdout and os.Stderr. The function waits for the command to complete before returning. Returns ErrCommandMissing if no command arguments are provided.
func Sh ¶
Sh executes a shell command using /bin/sh -c. This is only available on Unix-like systems (Linux, macOS, etc.). The command string is passed directly to the shell and can include pipes, redirects, and other shell features.
func ShQuote ¶
ShQuote safely quotes a string for use in shell commands. It wraps the string in single quotes and properly escapes any embedded single quotes using the '\” technique. This is useful for safely interpolating user input into shell commands.
Example:
cmd := "echo " + ShQuote(userInput) Sh(cmd)
Types ¶
type Env ¶ added in v0.2.3
type Env []string
Env represents a set of environment variables as a slice of "KEY=VALUE" strings. A nil Env indicates that the system's environment should be used.
func NewEnv ¶ added in v0.2.3
NewEnv creates a new Env with standard environment variables set. It initializes USER (derived from home path), PWD ("/"), HOME, and PATH with sensible defaults. Additional variables can be passed as "KEY=VALUE" strings. Duplicate keys are automatically deduplicated, keeping the last value.
func SysEnv ¶ added in v0.2.3
func SysEnv() Env
SysEnv returns a nil Env, which signals that the operating system's environment should be used when running commands. This is the default behavior and is more efficient than copying the system environment.
func (Env) Contains ¶ added in v0.2.3
Contains reports whether the Env contains the specified key. If the Env is nil, it checks the system environment.
func (Env) Dedup ¶ added in v0.2.3
Dedup returns a copy of the Env with duplicate keys removed. When duplicates exist, the last occurrence is kept, preserving the behavior of later values overriding earlier ones. Returns nil if the input Env is nil.
func (Env) Get ¶ added in v0.2.3
Get retrieves the value of an environment variable by key. Returns an empty string if the key is not found. If the Env is nil, it queries the system environment via os.Getenv.
func (Env) Join ¶ added in v0.2.3
Join merges multiple environments together into a new Env. If the receiver is nil, it starts with the system environment. No deduplication is performed; use Dedup afterwards if needed.
func (Env) Run ¶ added in v0.2.3
Run executes a command with the specified environment and waits for it to complete. The command's stdout and stderr are forwarded to os.Stdout and os.Stderr. If the Env is nil, the system environment is used. Returns ErrCommandMissing if no arguments are provided.
func (Env) RunGet ¶ added in v0.2.3
RunGet executes a command with the specified environment and returns its stdout as a byte slice. The function waits for the command to complete before returning. Returns ErrCommandMissing if no command arguments are provided.
func (Env) RunJson ¶ added in v0.2.3
RunJson executes a command with the specified environment and decodes its JSON stdout into the provided object. The obj parameter should be a pointer to the type you want to decode into. Returns ErrCommandMissing if no command arguments are provided.
func (Env) RunPipe ¶ added in v0.2.3
RunPipe executes a command with the specified environment in the background with both stdin and stdout connected. Data from the provided Reader is passed to the command's stdin, and the command's stdout is available through the returned Pipe. Always close the returned Pipe to release resources and prevent zombie processes. Returns ErrCommandMissing if no command arguments are provided.
func (Env) RunRead ¶ added in v0.2.3
RunRead executes a command with the specified environment in the background and returns its stdout as a Pipe. Always close the returned Pipe to release resources and prevent zombie processes. If the command fails, the error is returned when reading from the pipe at EOF. Returns ErrCommandMissing if no command arguments are provided.
func (Env) RunWrite ¶ added in v0.2.3
RunWrite executes a command with the specified environment and the provided Reader as stdin. The command's stdout and stderr are forwarded to os.Stdout and os.Stderr. The function waits for the command to complete before returning. Returns ErrCommandMissing if no command arguments are provided.
type LinuxProcState ¶ added in v0.2.0
type LinuxProcState struct {
Pid int // Process ID
Comm string // Executable name (e.g., "bash")
State byte // Process state: 'R' (running), 'S' (sleeping), 'D' (disk sleep), 'Z' (zombie), 'T' (stopped), etc.
PPid int // Parent process ID
PGrp int // Process group ID
Session int // Session ID
TtyNr int // Controlling terminal
Tpgid int // Foreground process group ID of controlling terminal
Flags uint // Kernel flags (PF_*)
Minflt uint64 // Minor faults (no disk I/O)
Cminflt uint64 // Minor faults by waited-for children
Majflt uint64 // Major faults (required disk I/O)
Cmajflt uint64 // Major faults by waited-for children
Utime uint64 // User mode CPU time in clock ticks
Stime uint64 // Kernel mode CPU time in clock ticks
Cutime uint64 // User mode CPU time of waited-for children
Cstime uint64 // Kernel mode CPU time of waited-for children
Priority int64 // Scheduling priority
Nice int64 // Nice value: 19 (low priority) to -20 (high priority)
NumThreads int64 // Number of threads in the process
Itrealvalue int64 // Time in jiffies before the next SIGALRM
StartTime uint64 // Process start time after system boot (in clock ticks)
Vsize uint64 // Virtual memory size in bytes
RSS int64 // Resident set size (pages in real memory)
RSSlim uint64 // Current soft limit on RSS in bytes
}
LinuxProcState contains detailed process information parsed from /proc/<pid>/stat. This is only available on Linux systems. The fields correspond to the columns documented in the procfs(5) man page.
func LinuxPidState ¶ added in v0.2.0
func LinuxPidState(pid uint64) (*LinuxProcState, error)
LinuxPidState returns detailed Linux-specific process information for the given PID. It reads and parses /proc/<pid>/stat to populate the LinuxProcState struct. Returns an error if the process does not exist or the stat file cannot be parsed.
func (*LinuxProcState) IsRunning ¶ added in v0.2.0
func (s *LinuxProcState) IsRunning() bool
IsRunning reports whether the process is currently in the running state ('R'). Note that 'R' means the process is either running or runnable (in the run queue).
type Pipe ¶ added in v0.0.4
type Pipe interface {
io.ReadCloser
// CopyTo copies all data from the pipe to the provided Writer.
// It returns the number of bytes copied and any error encountered.
// The process exit status is checked after all data is read.
CopyTo(io.Writer) (int64, error)
// CloseWait closes the pipe and waits for the process to exit.
// If the context has a deadline, the process is killed when the
// deadline expires. This allows for graceful shutdown with a timeout.
CloseWait(ctx context.Context) error
}
Pipe extends io.ReadCloser with additional methods for managing process output streams. It is returned by RunRead and RunPipe.
Unlike a standard io.ReadCloser, a Pipe properly handles process lifecycle and error propagation. When the underlying process fails, the error is returned on the final Read call (at EOF), ensuring that command failures are not silently ignored.
func RunPipe ¶
RunPipe executes a command in the background with both stdin and stdout connected. Data from the provided Reader is passed to the command's stdin, and the command's stdout is available through the returned Pipe. This enables chaining commands in a pipeline, similar to Unix shell pipes. Always close the returned Pipe to release resources and prevent zombie processes. Returns ErrCommandMissing if no command arguments are provided.
func RunRead ¶
RunRead executes a command in the background and returns its stdout as a Pipe. The command continues running until the pipe is closed or EOF is reached. Always close the returned Pipe to release resources and prevent zombie processes. If the command fails, the error is returned when reading from the pipe at EOF. Returns ErrCommandMissing if no command arguments are provided.
type ProcState ¶ added in v0.2.0
type ProcState interface {
// IsRunning reports whether the process is currently running (state 'R').
IsRunning() bool
// Started returns the time when the process was started.
Started() (time.Time, error)
}
ProcState provides information about a running process. On Linux, this is implemented by LinuxProcState with full process details. On other platforms, PidState returns ErrNotSupported.