Skip to main content
The process module provides comprehensive functions for process management, monitoring, control, and background job handling.

Query functions

Functions to check process status and retrieve process information.
Check if a process is running by PID.
process.sh:11
process::is_running() {
    kill -0 "$1" 2>/dev/null
}
Usage:
if process::is_running 1234; then
  echo "Process is running"
fi
Check if a process is running by name.
process.sh:17
process::is_running::name() {
    pgrep -x "$1" >/dev/null 2>&1
}
Usage:
if process::is_running::name nginx; then
  echo "Nginx is running"
fi
Get PID(s) of a named process (one per line).
process.sh:23
process::pid() {
    pgrep -x "$1" 2>/dev/null
}
Usage:
for pid in $(process::pid nginx); do
  echo "Found nginx process: $pid"
done
Get parent PID of a process.
process.sh:29
process::ppid() {
    local pid="${1:-$$}"
    awk '{print $4}' "/proc/$pid/stat" 2>/dev/null || \
        ps -o ppid= -p "$pid" 2>/dev/null | tr -d ' '
}
Get PID of current shell.
process.sh:36
process::self() {
    echo "$$"
}
Get process name from PID.
process.sh:42
process::name() {
    local pid="${1:-$$}"
    if [[ -f "/proc/$pid/comm" ]]; then
        cat "/proc/$pid/comm"
    else
        ps -o comm= -p "$pid" 2>/dev/null
    fi
}
Get command line of a process.
process.sh:53
process::cmdline() {
    local pid="${1:-$$}"
    if [[ -f "/proc/$pid/cmdline" ]]; then
        tr '\0' ' ' < "/proc/$pid/cmdline"
    else
        ps -o args= -p "$pid" 2>/dev/null
    fi
}
Usage:
echo "Command: $(process::cmdline 1234)"
Get process state (R=running, S=sleeping, Z=zombie, etc.).
process.sh:64
process::state() {
    local pid="$1"
    if [[ -f "/proc/$pid/status" ]]; then
        awk '/^State:/{print $2}' "/proc/$pid/status"
    else
        ps -o state= -p "$pid" 2>/dev/null
    fi
}
Possible states:
  • R - Running
  • S - Sleeping
  • D - Disk sleep
  • Z - Zombie
  • T - Stopped
Check if a process is a zombie.
process.sh:74
process::is_zombie() {
    [[ "$(process::state "$1")" == "Z" ]]
}
Get process working directory.
process.sh:80
process::cwd() {
    local pid="${1:-$$}"
    readlink "/proc/$pid/cwd" 2>/dev/null || \
        lsof -p "$pid" 2>/dev/null | awk '$4=="cwd"{print $9}'
}
Get process environment variable.
process.sh:88
process::env() {
    local pid="$1" var="$2"
    if [[ -f "/proc/$pid/environ" ]]; then
        tr '\0' '\n' < "/proc/$pid/environ" | grep "^${var}=" | cut -d= -f2-
    fi
}
Usage:
path=$(process::env 1234 PATH)
echo "Process PATH: $path"
List all running processes (PID and name).
process.sh:96
process::list() {
    ps -eo pid,comm --no-headers 2>/dev/null || \
        ps -eo pid,comm 2>/dev/null | tail -n +2
}
Find processes matching a pattern (name or cmdline).
process.sh:103
process::find() {
    pgrep -a "$1" 2>/dev/null || ps -eo pid,args | grep "$1" | grep -v grep
}
Usage:
process::find nginx
process::find "python.*server"
Get process tree from a PID.
process.sh:109
process::tree() {
    local pid="${1:-1}"
    if runtime::has_command pstree; then
        pstree -p "$pid"
    else
        ps -eo pid,ppid,comm | awk -v root="$pid" '
            NR==1{next}
            {parent[$1]=$2; name[$1]=$3}
            function show(p, indent,    c) {
                print indent p " " name[p]
                for (c in parent)
                    if (parent[c]==p) show(c, indent "  ")
            }
            END{show(root, "")}
        '
    fi
}

Resource usage

Functions to monitor process resource consumption.
Get CPU usage percentage for a PID.
process.sh:133
process::cpu() {
    ps -o pcpu= -p "$1" 2>/dev/null | tr -d ' '
}
Usage:
echo "CPU usage: $(process::cpu 1234)%"
Get memory usage in KB for a PID.
process.sh:139
process::memory() {
    if [[ -f "/proc/$1/status" ]]; then
        awk '/^VmRSS:/{print $2}' "/proc/$1/status"
    else
        ps -o rss= -p "$1" 2>/dev/null | tr -d ' '
    fi
}
Get memory usage as percentage.
process.sh:148
process::memory::percent() {
    ps -o pmem= -p "$1" 2>/dev/null | tr -d ' '
}
Get number of open file descriptors for a PID.
process.sh:153
process::fd_count() {
    ls "/proc/$1/fd" 2>/dev/null | wc -l
}
Get number of threads for a PID.
process.sh:158
process::thread_count() {
    if [[ -f "/proc/$1/status" ]]; then
        awk '/^Threads:/{print $2}' "/proc/$1/status"
    else
        ps -o nlwp= -p "$1" 2>/dev/null | tr -d ' '
    fi
}
Get process start time (unix timestamp).
process.sh:167
process::start_time() {
    local pid="$1"
    if runtime::has_command ps; then
        ps -o lstart= -p "$pid" 2>/dev/null
    fi
}
Get process uptime in seconds.
process.sh:175
process::uptime() {
    local pid="$1"
    if [[ -f "/proc/$pid/stat" ]]; then
        local clk_tck start_ticks uptime_secs
        clk_tck=$(getconf CLK_TCK 2>/dev/null || echo 100)
        start_ticks=$(awk '{print $22}' "/proc/$pid/stat")
        uptime_secs=$(awk '{print $1}' /proc/uptime)
        echo "$(( ${uptime_secs%.*} - start_ticks / clk_tck ))"
    fi
}

Control functions

Functions to send signals and control process execution.
Send a signal to a process.
process.sh:192
process::signal() {
    kill -"$2" "$1" 2>/dev/null
}
Usage:
process::signal 1234 TERM
process::signal 1234 USR1
Terminate a process (SIGTERM).
process.sh:197
process::kill() {
    kill -TERM "$1" 2>/dev/null
}
Force kill a process (SIGKILL).
process.sh:202
process::kill::force() {
    kill -KILL "$1" 2>/dev/null
}
Kill all processes matching a name.
process.sh:207
process::kill::name() {
    pkill -x "$1" 2>/dev/null
}
Graceful kill — SIGTERM, wait, then SIGKILL if still running.
process.sh:213
process::kill::graceful() {
    local pid="$1" timeout="${2:-5}"
    process::is_running "$pid" || return 0

    kill -TERM "$pid" 2>/dev/null

    local elapsed=0
    while (( elapsed < timeout )); do
        process::is_running "$pid" || return 0
        sleep 1
        (( elapsed++ ))
    done

    # Still running after timeout — force kill
    kill -KILL "$pid" 2>/dev/null
    sleep 1
    process::is_running "$pid" && return 1 || return 0
}
Usage:
# Give process 10 seconds to terminate gracefully
process::kill::graceful 1234 10
Suspend a process (SIGSTOP).
process.sh:233
process::suspend() {
    kill -STOP "$1" 2>/dev/null
}
Resume a suspended process (SIGCONT).
process.sh:238
process::resume() {
    kill -CONT "$1" 2>/dev/null
}
Reload a process config (SIGHUP).
process.sh:243
process::reload() {
    kill -HUP "$1" 2>/dev/null
}
Wait for a process to finish.
process.sh:249
process::wait() {
    local pid="$1" timeout="${2:-}"
    if [[ -z "$timeout" ]]; then
        wait "$pid" 2>/dev/null
        return $?
    fi

    local elapsed=0
    while process::is_running "$pid"; do
        sleep 1
        (( elapsed++ ))
        (( elapsed >= timeout )) && return 1
    done
    return 0
}
Usage:
# Wait indefinitely
process::wait 1234

# Wait with timeout (30 seconds)
process::wait 1234 30
Change process priority (nice value, -20 to 19).
process.sh:267
process::renice() {
    renice -n "$2" -p "$1" 2>/dev/null
}
Usage:
# Lower priority (higher nice value)
process::renice 1234 10

# Higher priority (requires root)
process::renice 1234 -5

Background jobs

Functions for managing background processes.
Run a command in the background, print its PID.
process.sh:277
process::run_bg() {
    "$@" &
    echo $!
}
Usage:
pid=$(process::run_bg sleep 60)
echo "Background job PID: $pid"
Run a command in the background, redirect output to a log file.
process.sh:284
process::run_bg::log() {
    local logfile="$1"; shift
    "$@" >> "$logfile" 2>&1 &
    echo $!
}
Usage:
pid=$(process::run_bg::log /tmp/job.log long-running-command --args)
Run a command in the background with a timeout.
process.sh:292
process::run_bg::timeout() {
    local timeout="$1"; shift
    (
        "$@" &
        local pid=$!
        sleep "$timeout"
        process::kill::graceful "$pid"
    ) &
    echo $!
}
List current shell’s background jobs.
process.sh:304
process::job::list() {
    jobs -l
}
Wait for all background jobs to finish.
process.sh:309
process::job::wait_all() {
    wait
}
Wait for a specific background job by PID.
process.sh:314
process::job::wait() {
    wait "$1" 2>/dev/null
    return $?
}
Get exit status of last background job.
process.sh:320
process::job::status() {
    wait "$1" 2>/dev/null
    echo $?
}

Locking

Functions to prevent concurrent execution using file-based locks.
Acquire a lock — returns 1 if already locked.
process.sh:332
process::lock::acquire() {
    local lockfile="/tmp/fsbshf_${1}.lock"
    if ( set -o noclobber; echo "$$" > "$lockfile" ) 2>/dev/null; then
        trap "process::lock::release '${1}'" EXIT
        return 0
    fi
    # Check if the locking process is still alive
    local locked_pid
    locked_pid=$(cat "$lockfile" 2>/dev/null)
    if [[ -n "$locked_pid" ]] && ! process::is_running "$locked_pid"; then
        rm -f "$lockfile"
        ( set -o noclobber; echo "$$" > "$lockfile" ) 2>/dev/null
        trap "process::lock::release '${1}'" EXIT
        return 0
    fi
    return 1
}
Usage:
if process::lock::acquire myapp; then
  echo "Lock acquired"
  # Do work
else
  echo "Already running"
  exit 1
fi
Release a lock.
process.sh:352
process::lock::release() {
    rm -f "/tmp/fsbshf_${1}.lock"
}
Check if a lock is held.
process.sh:358
process::lock::is_locked() {
    local lockfile="/tmp/fsbshf_${1}.lock"
    [[ -f "$lockfile" ]] && process::is_running "$(cat "$lockfile" 2>/dev/null)"
}
Wait for a lock to become available.
process.sh:365
process::lock::wait() {
    local name="$1" timeout="${2:-30}" elapsed=0
    while ! process::lock::acquire "$name"; do
        sleep 1
        (( elapsed++ ))
        (( elapsed >= timeout )) && return 1
    done
    return 0
}
Usage:
# Wait up to 60 seconds for lock
if process::lock::wait myapp 60; then
  echo "Lock acquired"
else
  echo "Timeout waiting for lock"
fi

Service management

Functions to manage systemd/init services.
Check if a systemd service is running.
process.sh:381
process::service::is_running() {
    if runtime::has_command systemctl; then
        systemctl is-active --quiet "$1" 2>/dev/null
    elif runtime::has_command service; then
        service "$1" status >/dev/null 2>&1
    else
        process::is_running::name "$1"
    fi
}
Start a systemd service.
process.sh:392
process::service::start() {
    if runtime::has_command systemctl; then
        systemctl start "$1"
    elif runtime::has_command service; then
        service "$1" start
    fi
}
Stop a systemd service.
process.sh:401
process::service::stop() {
    if runtime::has_command systemctl; then
        systemctl stop "$1"
    elif runtime::has_command service; then
        service "$1" stop
    fi
}
Restart a systemd service.
process.sh:410
process::service::restart() {
    if runtime::has_command systemctl; then
        systemctl restart "$1"
    elif runtime::has_command service; then
        service "$1" restart
    fi
}
Check if a service is enabled at boot.
process.sh:419
process::service::is_enabled() {
    if runtime::has_command systemctl; then
        systemctl is-enabled --quiet "$1" 2>/dev/null
    fi
}

Utilities

Miscellaneous process utilities.
Run a command and return its execution time in seconds.
process.sh:431
process::time() {
    local start end
    start=$(date +%s%N 2>/dev/null || date +%s)
    "$@"
    local ret=$?
    end=$(date +%s%N 2>/dev/null || date +%s)
    # nanosecond precision if available
    if [[ "${#start}" -gt 10 ]]; then
        echo "$(( (end - start) / 1000000 ))ms"
    else
        echo "$(( end - start ))s"
    fi
    return $ret
}
Usage:
time_taken=$(process::time sleep 2)
echo "Took $time_taken"
Run a command with a timeout, kill it if it exceeds.
process.sh:448
process::timeout() {
    local timeout="$1"; shift
    if runtime::has_command timeout; then
        timeout "$timeout" "$@"
    else
        # Pure bash fallback
        "$@" &
        local pid=$!
        ( sleep "$timeout"; process::kill::graceful "$pid" ) &
        local watcher=$!
        wait "$pid" 2>/dev/null
        local ret=$?
        kill "$watcher" 2>/dev/null
        return $ret
    fi
}
Usage:
# Kill after 10 seconds
process::timeout 10 long-running-command
Retry a command n times with a delay between attempts.
process.sh:467
process::retry() {
    local tries="$1" delay="$2"; shift 2
    local attempt=0
    while (( attempt < tries )); do
        "$@" && return 0
        (( attempt++ ))
        (( attempt < tries )) && sleep "$delay"
    done
    return 1
}
Usage:
# Try 5 times with 2 second delay
process::retry 5 2 curl https://example.com
Run command only if not already running (singleton).
process.sh:480
process::singleton() {
    local name="$1"; shift
    if process::lock::acquire "$name"; then
        "$@"
    else
        echo "process::singleton: '$name' is already running" >&2
        return 1
    fi
}
Usage:
process::singleton backup-job ./run-backup.sh

Build docs developers (and LLMs) love