developing-complex-posix-shell-scripts

developing-complex-posix-shell-scripts skill

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "developing-complex-posix-shell-scripts" with this command: npx skills add ak1ra-komj/agents-skills/ak1ra-komj-agents-skills-developing-complex-posix-shell-scripts

developing-complex-posix-shell-scripts skill

This skill defines guidelines for writing complex POSIX shell scripts: production utilities, reusable system tools, or scripts that require multiple flags, structured logging, or robust error handling — while remaining strictly portable.

Use developing-simple-posix-shell-scripts for ad-hoc tasks or scripts under ~50 lines without CLI scaffolding.

When to Use This Skill

  • Reusable system utilities or production automation scripts targeting /bin/sh

  • Scripts with multiple named flags / options

  • Requires structured logging with severity levels

  • Needs -h / help output

  • Has cleanup logic, dependency checks, or complex control flow

  • Must run portably on Alpine, BusyBox, embedded, or other minimal systems

Core Requirements

Shebang & Safety Modes

#!/bin/sh

set -e set -u

  • set -e : exit immediately on error.

  • set -u : exit on reference to an unset variable.

  • set -o pipefail is not POSIX — do not use it.

Tooling

  • All scripts MUST pass shellcheck --shell=sh without warnings.

  • Format with shfmt -ln posix before considering the script done.

POSIX Compliance — Do NOT Use These Bash-isms

Bash feature POSIX replacement

[[ ... ]]

[ ... ]

local var

prefix with _funcname_var (see Variables section)

declare -a arr

not available — restructure logic

source file

. file

function f { }

f() { }

(( expr ))

$(( expr ))

$'...' strings printf

<<< here-strings printf ... | or temp file

<(cmd) process substitution temp file or pipe

echo -e

printf

Variables & Quoting

  • Always use ${var} (braces) for variable expansion.

  • Always quote expansions: "${var}" .

  • POSIX sh has no local keyword. Simulate function-local variables by prefixing with the function name: _log_message_color , _parse_args_opt , etc. Unset them at the end of the function if needed.

  • Use $(...) for command substitution, never backticks.

  • Prefer printf over echo for reliable, portable output.

Reference Code Blocks

Pick and compose the sections you need. Do NOT copy everything blindly — only include what the script actually uses.

  1. Script Identity

SCRIPT_NAME="$(basename "${0}")"

  1. Logging Subsystem

Use this block when the script needs more than simple printf output. Because POSIX sh has no associative arrays, level priorities are resolved via a helper function.

Logging configuration

LOG_LEVEL="INFO" # ERROR, WARNING, INFO, DEBUG LOG_FORMAT="simple" # simple, level, full

get_log_priority() { case "${1}" in DEBUG) printf '10' ;; INFO) printf '20' ;; WARNING) printf '30' ;; ERROR) printf '40' ;; CRITICAL) printf '50' ;; *) printf '0' ;; esac }

log_color() { _log_color_code="${1}" shift if [ -t 2 ]; then # shellcheck disable=SC2059 printf "\033[0;%sm%s\033[0m\n" "${_log_color_code}" "${}" >&2 else printf '%s\n' "${}" >&2 fi unset _log_color_code }

log_message() { _log_message_color="${1}" _log_message_level="${2}" shift 2

_log_message_current_prio=$(get_log_priority "${LOG_LEVEL}")
_log_message_msg_prio=$(get_log_priority "${_log_message_level}")

if [ "${_log_message_msg_prio}" -lt "${_log_message_current_prio}" ]; then
    unset _log_message_color _log_message_level _log_message_current_prio _log_message_msg_prio
    return 0
fi

case "${LOG_FORMAT}" in
    simple) log_color "${_log_message_color}" "${*}" ;;
    level)  log_color "${_log_message_color}" "[${_log_message_level}] ${*}" ;;
    full)
        _log_message_ts=$(date '+%Y-%m-%dT%H:%M:%S' 2>/dev/null || printf 'unknown')
        log_color "${_log_message_color}" "[${_log_message_ts}][${_log_message_level}] ${*}"
        unset _log_message_ts
        ;;
    *)      log_color "${_log_message_color}" "${*}" ;;
esac
unset _log_message_color _log_message_level _log_message_current_prio _log_message_msg_prio

}

log_error() { log_message '31' 'ERROR' "${@}"; } log_info() { log_message '32' 'INFO' "${@}"; } log_warning() { log_message '33' 'WARNING' "${@}"; } log_debug() { log_message '34' 'DEBUG' "${@}"; } log_critical() { log_message '36' 'CRITICAL' "${@}"; }

  1. Log Level & Format Setters

Include these when the script exposes -l / log-format flags.

set_log_level() { # tr to uppercase without bashism _set_log_level_val=$(printf '%s' "${1}" | tr '[:lower:]' '[:upper:]') case "${_set_log_level_val}" in DEBUG | INFO | WARNING | ERROR | CRITICAL) LOG_LEVEL="${_set_log_level_val}" ;; *) log_error "Invalid log level: ${1}. Valid levels: DEBUG, INFO, WARNING, ERROR, CRITICAL" exit 1 ;; esac unset _set_log_level_val }

set_log_format() { case "${1}" in simple | level | full) LOG_FORMAT="${1}" ;; *) log_error "Invalid log format: ${1}. Valid formats: simple, level, full" exit 1 ;; esac }

  1. Dependency Check

Include this when the script relies on external commands that may not be present.

require_command() { _require_command_missing='' for _require_command_c in "${@}"; do if ! command -v "${_require_command_c}" >/dev/null 2>&1; then _require_command_missing="${_require_command_missing} ${_require_command_c}" fi done

if [ -n "${_require_command_missing}" ]; then
    log_error "Required command(s) not installed:${_require_command_missing}"
    log_error "Please install the missing dependencies and try again"
    exit 1
fi
unset _require_command_missing _require_command_c

}

  1. Cleanup Handler

Include this when the script creates temporary files or resources that must be cleaned up on exit.

cleanup() { _cleanup_exit_code=$? # Add cleanup logic here (e.g., rm -f "${_tmpfile:-}") exit "${_cleanup_exit_code}" }

trap cleanup EXIT INT TERM

  1. Usage / Help

Argument ordering convention: list template flags first (-h , -l , -f ), then script-specific flags. This mirrors the ordering required in the getopts optstring and case statement (see Section 7), keeping all three in sync.

usage() { _usage_exit_code="${1:-0}" cat <<EOF USAGE: ${SCRIPT_NAME} [OPTIONS]

Short description of what the script does.

OPTIONS: -h Show this help message -l LEVEL Set log level (DEBUG, INFO, WARNING, ERROR, CRITICAL) Default: INFO -f FORMAT Set log format (simple, level, full) simple: message only level: [LEVEL] message full: [timestamp][LEVEL] message Default: simple <script-specific flags go here>

EXAMPLES: ${SCRIPT_NAME} -h ${SCRIPT_NAME} -l DEBUG -f full

EOF exit "${_usage_exit_code}" }

  1. Argument Parsing

Use getopts (POSIX built-in, short options only). Adjust the optstring to match the script's flags.

Argument ordering convention: template flags come first in the optstring and case statement, script-specific flags come after. This keeps usage , getopts optstring, and case in sync.

parse_args() { # --- template flag defaults --- # LOG_LEVEL and LOG_FORMAT already defaulted at top of script

# --- script-specific flag defaults ---
# MY_FLAG="default"

# template flags first (hl:f:), then script-specific flags
while getopts ':hl:f:' _parse_args_opt; do
    case "${_parse_args_opt}" in
        # --- template flags (always first) ---
        h) usage 0 ;;
        l) set_log_level "${OPTARG}" ;;
        f) set_log_format "${OPTARG}" ;;
        # --- script-specific flags ---
        \?)
            log_error "Invalid option: -${OPTARG}"
            usage 1
            ;;
        :)
            log_error "Option -${OPTARG} requires an argument"
            usage 1
            ;;
    esac
done
shift $((OPTIND - 1))

# Remaining positional arguments are now in "$@"

}

  1. Main Entry Point

main() { parse_args "${@}"

log_debug "Log level: ${LOG_LEVEL}, Log format: ${LOG_FORMAT}"

# Script logic here

}

main "${@}"

Composition Guide

  • Always include: Script Identity + Shebang/Safety Modes + main .

  • Include Logging Subsystem when output needs severity levels or colour.

  • Include Log Level/Format Setters only when exposing -l / -f flags.

  • Include Dependency Check when relying on non-standard external commands.

  • Include Cleanup Handler when the script allocates resources (temp files, locks, etc.).

  • Include Usage + Argument Parsing when the script accepts any named flags.

Compose only the sections the script actually needs. A complex script with no temp files does not need a cleanup handler.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

developing-ansible

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

developing-bash-scripts

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

developing-posix-shell-scripts

No summary provided by upstream source.

Repository SourceNeeds Review