| |

Bash Scripting 30 ๐Ÿง Advanced CLI Utilities โ€” Structuring enterprise-ready administrative tools

โฑ๏ธ Estimated read: 13 min | |

A script that starts as a single file with a few flags eventually becomes a tool that other people depend on. At that point, the concerns that were invisible in a small script become visible: how subcommands are organized, how options are parsed consistently, how logging is structured, how failures are reported, and how the tool behaves when it is invoked with no arguments or with an unknown subcommand. The difference between a script and an enterprise-ready utility is not the language; it is the structure.

This chapter covers the patterns that turn a Bash script into a maintainable administrative tool: the dispatcher pattern that routes subcommands to separate files or functions, the option parsing approach that handles both short and long options, the logging and error-handling conventions that make failures diagnosable, and the set -euo pipefail strict mode that prevents silent failures. By the end, you will understand how to structure a tool that another administrator can read, extend, and trust.

Key point: Enterprise-ready does not mean complex. It means predictable. A tool with a clear subcommand structure, consistent option parsing, structured logging, and strict error handling is enterprise-ready regardless of how many features it has.


Why structure matters more than features

The single-file growth problem. A script that begins as backup.sh with a few hardcoded paths becomes a file with hundreds of lines, nested conditionals, and option parsing scattered across the top. Adding a new capability requires understanding the entire file. The dispatcher pattern separates each subcommand into its own function or file, so a new capability is a new unit that does not require reading the existing ones .

The option parsing inconsistency problem. Bash’s builtin getopts handles short options (-v, -o file) but does not support long options (--verbose, --output file) . A tool that needs --help and --version in addition to short flags must either use the external GNU getopt command, which supports long options on Linux, or implement a manual while loop that handles both forms. The choice affects portability: GNU getopt is not available by default on macOS or FreeBSD .

The logging and error problem. A script that prints errors to stderr with echo and exits with status 1 is usable, but it does not tell the operator which command failed, on which line, or what the exit status of the failing command was. A logging function that includes a timestamp, a severity level, and the script name is the minimum for a tool that runs unattended. The set -euo pipefail header is the minimum for a tool that should fail fast rather than continue after an error .


a. The dispatcher pattern for subcommands

The dispatcher pattern routes the first argument to a function or a separate script file. For small tools, a case statement with a function per subcommand is sufficient. For larger tools, separate files in a subcommands/ directory keep each command isolated.

#!/bin/bash
set -euo pipefail

usage() {
    cat <<EOF
Usage: $(basename "$0") <command> [options]

Commands:
  backup    Create a backup of the specified directory
  restore   Restore from a backup archive
  verify    Verify the integrity of a backup archive
  help      Show this help message
EOF
}

cmd_backup() {
    echo "Running backup with args: $*"
    # implementation
}

cmd_restore() {
    echo "Running restore with args: $*"
    # implementation
}

cmd_verify() {
    echo "Running verify with args: $*"
    # implementation
}

main() {
    if [[ $# -eq 0 ]]; then
        usage
        exit 1
    fi

    local command="$1"
    shift

    case "$command" in
        backup)  cmd_backup "$@" ;;
        restore) cmd_restore "$@" ;;
        verify)  cmd_verify "$@" ;;
        help|-h|--help) usage; exit 0 ;;
        *)
            echo "Unknown command: $command" >&2
            usage >&2
            exit 1
            ;;
    esac
}

main "$@"

The dispatcher separates the command name from its arguments. Each subcommand function receives only its own arguments and parses them independently. This isolation is what allows a new subcommand to be added without touching the existing ones .

For a file-based dispatcher, the main script locates a subcommands/ directory relative to its own path and executes the matching file. This pattern, used by tools like git, allows each subcommand to be a separate script in any language .

#!/bin/bash
set -euo pipefail

SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
export PROG="$0"

if [[ $# -eq 0 ]]; then
    echo "Usage: $PROG <subcommand> [args...]" >&2
    ls "$SCRIPT_DIR/subcommands"
    exit 1
fi

subcommand="$1"
shift

if [[ -x "$SCRIPT_DIR/subcommands/$subcommand" ]]; then
    exec "$SCRIPT_DIR/subcommands/$subcommand" "$@"
else
    echo "Unknown subcommand: $subcommand" >&2
    exit 1
fi

The exec replaces the main script with the subcommand, which avoids an extra process in the chain. The PROG environment variable lets subcommands call each other through the main dispatcher .

b. Option parsing for both short and long options

The builtin getopts is portable and handles short options correctly, including arguments with embedded spaces. It does not support long options. The external GNU getopt supports long options but is not available on all platforms .

For a tool that must handle both, the manual while loop is the most portable approach. It processes $1 directly, shifts as it goes, and handles --long, -s, and positional arguments in one loop.

parse_backup_args() {
    local target=""
    local output=""
    local compress=false

    while [[ $# -gt 0 ]]; do
        case "$1" in
            -o|--output)
                output="$2"
                shift 2
                ;;
            -c|--compress)
                compress=true
                shift
                ;;
            -h|--help)
                backup_usage
                exit 0
                ;;
            -*)
                echo "Unknown option: $1" >&2
                backup_usage >&2
                exit 1
                ;;
            *)
                target="$1"
                shift
                ;;
        esac
    done

    if [[ -z "$target" ]]; then
        echo "Error: target directory is required" >&2
        backup_usage >&2
        exit 1
    fi

    echo "Target: $target, Output: $output, Compress: $compress"
}

This loop handles -o file, --output file, --output=file (if the case pattern includes --output=*), combined short flags are not handled by this pattern and require a separate getopts pass if needed. The trade-off is explicit: the manual loop is more code, but it is portable and handles both option forms without depending on GNU getopt.

For tools that only need short options, getopts is the correct choice because it handles the edge cases correctly and produces standard error messages .

c. Logging, strict mode, and error reporting

A structured logging function turns opaque failures into diagnosable events. The function should include a timestamp, a severity level, and the script name, and it should write to stderr for warnings and errors so that the output is visible even when stdout is redirected.

log() {
    local level="$1"
    shift
    local message="$*"
    local timestamp
    timestamp=$(date -u '+%Y-%m-%dT%H:%M:%SZ')
    printf '%s [%s] %s: %s\n' "$timestamp" "$level" "${0##*/}" "$message" >&2
}

log_info()  { log INFO "$@"; }
log_warn()  { log WARN "$@"; }
log_error() { log ERROR "$@"; }

The set -euo pipefail header should be at the top of every script. set -e exits on a failing command, set -u errors on an unset variable, and set -o pipefail makes a pipeline fail if any stage fails, not just the last one . Without pipefail, a pipeline that includes a failing command can return success because the last command succeeded.

The ERR trap can report which command failed and on which line, which is useful when set -e terminates the script unexpectedly.

trap 'log_error "Command failed on line $LINENO: $BASH_COMMAND"' ERR

This trap runs whenever a command fails in a context that would cause set -e to exit. The $LINENO and $BASH_COMMAND variables identify the location and the command, which is more useful than a bare exit status.


Complete Example Session

# ============================================
# PART 1: THE STRICT MODE HEADER
# ============================================
#!/bin/bash
set -euo pipefail

# -e: exit on error
# -u: error on unset variable
# -o pipefail: fail if any pipeline stage fails
# ============================================
# PART 2: THE LOGGING FUNCTIONS
# ============================================
log() {
    local level="$1"; shift
    local message="$*"
    local timestamp
    timestamp=$(date -u '+%Y-%m-%dT%H:%M:%SZ')
    printf '%s [%s] %s: %s\n' "$timestamp" "$level" "${0##*/}" "$message" >&2
}
log_info()  { log INFO "$@"; }
log_error() { log ERROR "$@"; }
# ============================================
# PART 3: THE USAGE FUNCTION
# ============================================
usage() {
    cat <<EOF
Usage: $(basename "$0") <command> [options]

Commands:
  backup    Create a backup
  restore   Restore from a backup
  verify    Verify a backup
EOF
}
# ============================================
# PART 4: THE DISPATCHER
# ============================================
main() {
    if [[ $# -eq 0 ]]; then
        usage
        exit 1
    fi
    local command="$1"; shift
    case "$command" in
        backup)  cmd_backup "$@" ;;
        restore) cmd_restore "$@" ;;
        verify)  cmd_verify "$@" ;;
        help|-h|--help) usage; exit 0 ;;
        *) log_error "Unknown command: $command"; usage >&2; exit 1 ;;
    esac
}
# ============================================
# PART 5: A SUBCOMMAND WITH OPTION PARSING
# ============================================
cmd_backup() {
    local target="" output="" compress=false
    while [[ $# -gt 0 ]]; do
        case "$1" in
            -o|--output) output="$2"; shift 2 ;;
            -c|--compress) compress=true; shift ;;
            -h|--help) usage; exit 0 ;;
            -*) log_error "Unknown option: $1"; exit 1 ;;
            *) target="$1"; shift ;;
        esac
    done
    [[ -z "$target" ]] && { log_error "Target required"; exit 1; }
    log_info "Backing up $target to ${output:-default}"
}
# ============================================
# PART 6: THE ERR TRAP
# ============================================
trap 'log_error "Failed on line $LINENO: $BASH_COMMAND"' ERR

# Runs when set -e would exit due to a failing command.
# ============================================
# PART 7: THE COMPLETE SCRIPT
# ============================================
#!/bin/bash
set -euo pipefail

log() {
    local level="$1"; shift
    printf '%s [%s] %s: %s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "$level" "${0##*/}" "$*" >&2
}
log_info()  { log INFO "$@"; }
log_error() { log ERROR "$@"; }
trap 'log_error "Failed on line $LINENO: $BASH_COMMAND"' ERR

usage() { echo "Usage: $0 <backup|restore|verify> [options]"; }

cmd_backup() {
    local target=""
    while [[ $# -gt 0 ]]; do
        case "$1" in
            -h|--help) usage; exit 0 ;;
            -*) log_error "Unknown option: $1"; exit 1 ;;
            *) target="$1"; shift ;;
        esac
    done
    [[ -z "$target" ]] && { log_error "Target required"; exit 1; }
    log_info "Backing up $target"
}

main() {
    [[ $# -eq 0 ]] && { usage; exit 1; }
    local cmd="$1"; shift
    case "$cmd" in
        backup) cmd_backup "$@" ;;
        help|-h|--help) usage; exit 0 ;;
        *) log_error "Unknown command: $cmd"; exit 1 ;;
    esac
}
main "$@"
# ============================================
# PART 8: TESTING THE SCRIPT
# ============================================
# ./tool.sh backup /data
# Output: 2026-11-05T14:30:00Z [INFO] tool.sh: Backing up /data

# ./tool.sh backup
# Output: 2026-11-05T14:30:01Z [ERROR] tool.sh: Target required

# ./tool.sh unknown
# Output: 2026-11-05T14:30:02Z [ERROR] tool.sh: Unknown command: unknown
# ============================================
# PART 9: FILE-BASED DISPATCHER (LARGER TOOLS)
# ============================================
#!/bin/bash
set -euo pipefail
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
export PROG="$0"

[[ $# -eq 0 ]] && { echo "Usage: $PROG <subcommand>"; exit 1; }
sub="$1"; shift
if [[ -x "$SCRIPT_DIR/subcommands/$sub" ]]; then
    exec "$SCRIPT_DIR/subcommands/$sub" "$@"
else
    echo "Unknown: $sub" >&2; exit 1
fi
# ============================================
# PART 10: DIRECTORY STRUCTURE
# ============================================
# tool.sh
# subcommands/
#   backup
#   restore
#   verify
#
# The main script locates the subcommand file
# and execs it with the remaining arguments.

The ten parts covered the strict mode header, logging functions, usage, the dispatcher, subcommand option parsing, the ERR trap, the complete script, testing, the file-based dispatcher, and the directory structure.


Quick Reference

Dispatcher patterns

PatternUse caseStructure
case + functionsSmall toolsSingle file
subcommands/ directoryLarger toolsOne file per command
exec in dispatcherAvoid extra processReplaces main script

Option parsing approaches

ApproachLong optionsPortabilityComplexity
getopts builtinNoHighLow
GNU getoptYesLinux onlyModerate
Manual while loopYesHighModerate

Logging function

ElementExample
Timestampdate -u '+%Y-%m-%dT%H:%M:%SZ'
LevelINFO, WARN, ERROR
Script name${0##*/}
Destination>&2

Strict mode

FlagEffect
set -eExit on failing command
set -uError on unset variable
set -o pipefailFail if any pipeline stage fails

Error reporting

MechanismPurpose
trap '...' ERRReport failing command and line
$LINENOLine number of failure
$BASH_COMMANDCommand that failed

Best Practices

โœ… Do This:

# Use strict mode at the top of every script
set -euo pipefail                                          # โœ…
# Use a dispatcher instead of a single long conditional block
case "$command" in backup) cmd_backup "$@" ;; esac         # โœ…
# Write logs to stderr with a timestamp and level
printf '%s [%s] %s\n' "$(date -u ...)" "$level" "$msg" >&2 # โœ…
# Report failing commands with the ERR trap
trap 'log_error "line $LINENO: $BASH_COMMAND"' ERR         # โœ…

โŒ Don’t Do This:

# Parse options with a single getopts loop and expect long options
getopts "vo:" opt  # does not handle --output                   # โŒ
# Use echo for errors without a timestamp or level
echo "Error: something failed" >&2                              # โŒ
# Put all subcommands in one giant case statement without functions
case "$1" in ... 200 lines ... esac                            # โŒ
# Skip set -e and continue after failures
false
echo "still running"                                           # โŒ

Common Pitfalls

PitfallWhy It HappensFix
Long options not recognizedgetopts does not support themUse GNU getopt or a manual while loop
Script continues after failureset -e not enabledAdd set -euo pipefail
Pipeline succeeds despite failurepipefail not setAdd set -o pipefail
Unknown command produces a stack traceNo *) case in dispatcherAdd a catch-all case with exit 1
Logs go to stdout and are lostNo >&2 on log functionRedirect logs to stderr
Subcommand file not foundDispatcher path wrongUse SCRIPT_DIR and exec

Real-World Examples

1. Strict mode header

set -euo pipefail

2. Logging function

log() { printf '%s [%s] %s: %s\n' "$(date -u +%FT%TZ)" "$1" "${0##*/}" "${*:2}" >&2; }

3. Dispatcher

case "$1" in backup) cmd_backup "${@:2}" ;; *) exit 1 ;; esac

4. Manual option loop

while [[ $# -gt 0 ]]; do case "$1" in -o|--output) out="$2"; shift 2 ;; *) shift ;; esac; done

5. ERR trap

trap 'log_error "line $LINENO: $BASH_COMMAND"' ERR

6. Usage function

usage() { echo "Usage: $0 <command>"; }

7. File-based dispatcher

exec "$SCRIPT_DIR/subcommands/$sub" "$@"

8. Subcommand directory

mkdir -p subcommands && touch subcommands/{backup,restore,verify}

9. Unknown command handling

*) log_error "Unknown: $1"; usage >&2; exit 1 ;;

10. Test a failure

./tool.sh backup /nonexistent  # should fail with a logged error

Visual

The dispatcher flow

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  DISPATCHER                                                  โ”‚
โ”‚                                                              โ”‚
โ”‚  ./tool.sh backup /data -o /backups                         โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  main()                                                      โ”‚
โ”‚    โ”œโ”€โ”€ $1 = "backup"                                         โ”‚
โ”‚    โ”œโ”€โ”€ shift                                                 โ”‚
โ”‚    โ””โ”€โ”€ case "backup" โ”€โ”€โ–บ cmd_backup "/data" "-o" "/backups" โ”‚
โ”‚                                                              โ”‚
โ”‚  cmd_backup parses its own arguments.                        โ”‚
โ”‚  No other subcommand is affected.                            โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Strict mode error reporting

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  SET -E AND ERR TRAP                                         โ”‚
โ”‚                                                              โ”‚
โ”‚  Script line 42: cp /nonexistent /tmp/                       โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  cp fails                                                    โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  set -e triggers exit                                        โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  ERR trap runs:                                              โ”‚
โ”‚    "line 42: cp /nonexistent /tmp/"                          โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  Script exits                                                โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Option parsing decision

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  WHICH PARSER?                                               โ”‚
โ”‚                                                              โ”‚
โ”‚  Need long options?                                          โ”‚
โ”‚    โ”‚                                                         โ”‚
โ”‚    โ”œโ”€โ”€ Yes โ”€โ”€โ–บ Need portability?                             โ”‚
โ”‚    โ”‚              โ”‚                                          โ”‚
โ”‚    โ”‚              โ”œโ”€โ”€ Yes โ”€โ”€โ–บ Manual while loop              โ”‚
โ”‚    โ”‚              โ”‚                                          โ”‚
โ”‚    โ”‚              โ””โ”€โ”€ No (Linux only) โ”€โ”€โ–บ GNU getopt         โ”‚
โ”‚    โ”‚                                                         โ”‚
โ”‚    โ””โ”€โ”€ No โ”€โ”€โ–บ getopts builtin                                โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

File-based structure

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  TOOL DIRECTORY                                              โ”‚
โ”‚                                                              โ”‚
โ”‚  tool.sh                    (main dispatcher)                โ”‚
โ”‚  subcommands/                                                โ”‚
โ”‚    backup                   (executable)                     โ”‚
โ”‚    restore                  (executable)                     โ”‚
โ”‚    verify                   (executable)                     โ”‚
โ”‚                                                              โ”‚
โ”‚  tool.sh backup /data                                        โ”‚
โ”‚    โ””โ”€โ”€ exec subcommands/backup /data                        โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ItemValue
Strict modeset -euo pipefail
Dispatchercase + functions or subcommands/ directory
Short optionsgetopts builtin
Long optionsGNU getopt or manual while loop
LoggingTimestamp + level + script name, stderr
Error reportingtrap '...' ERR with $LINENO and $BASH_COMMAND
File-based dispatchexec "$SCRIPT_DIR/subcommands/$sub" "$@"

Key takeaways:

  • Structure is the difference between a script and a tool. The dispatcher pattern separates subcommands so that each can be developed, tested, and understood independently. A new capability is a new function or file, not a modification to a growing conditional block .
  • getopts is portable but limited. It handles short options correctly and is available in every Bash. Long options require either GNU getopt (Linux-only) or a manual while loop (portable but more code) .
  • set -euo pipefail is the minimum for reliability. Without it, a failing command in a pipeline can go unnoticed and the script can continue with corrupt state .
  • Structured logging is not optional for unattended tools. A timestamp, a severity level, and the script name turn an opaque failure into an event that can be correlated with other logs and diagnosed after the fact.
  • The ERR trap reports the failing command and line. $LINENO and $BASH_COMMAND identify exactly what went wrong, which is more useful than a bare exit status.
  • The file-based dispatcher scales beyond a single file. For tools with many subcommands, placing each in its own executable file and using exec in the dispatcher keeps the main script small and lets subcommands be written in any language .

Remember: An enterprise-ready Bash tool is not defined by the number of features it has. It is defined by whether another administrator can read it, extend it, and trust it. The dispatcher pattern makes the structure obvious. The option parsing choice makes the interface predictable. The logging and error handling make failures diagnosable. And strict mode makes the tool stop when something goes wrong instead of continuing with corrupted state. Start with the dispatcher, add the logging function, enable strict mode, and add the ERR trap. The rest of the tool is just subcommands, each with its own arguments and its own logic.

Was this helpful?


Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!