-
Notifications
You must be signed in to change notification settings - Fork 0
args.sh
CLI option and command parsing via getopt. Defines your program's options, commands, and their arguments, then parses "$@" into structured data.
Bash requirement: 4.0+
External requirement: getopt (enhanced version, passes getopt --test)
Dependencies: none
Include guard: args::program
source "/path/to/options.bash/args.sh"
args::program "my-tool" "1.0.0" "A useful tool"
args::option "-v, --version" "Show version"
args::option "-h, --help" "Show help"
args::option "--output, -o" "Output file" "file"
args::option "-n" "Dry run"
args::option "build" "Build the project"
args::option "test" "Run tests" "suite"
args::parse "$@"Set program metadata. Called once before defining options.
args::program "deploy" "2.0.0" "Deploy to servers" "https://example.com/deploy"Register an option or command.
NAMES — comma and/or space-separated list of names:
- Names starting with
--are long options. - Names starting with
-(single character after dash) are short options. - Bare words (no leading dash) are commands.
DESCRIPTION — help text (may contain \n for multi-line).
ARG_NAME — if non-empty, the option/command takes an argument. The string is used as the argument's display name in help.
OPTIONAL — if non-empty, the argument is optional (only meaningful when ARG_NAME is set).
# Simple flag
args::option "-n, --dry-run" "Preview without executing"
# Option with required argument
args::option "--env, -e" "Target environment" "name"
# Option with optional argument
args::option "--color" "Color output" "when" optional
# Short option with required argument
args::option "-o" "Output file" "path"
# Command
args::option "build" "Build the project"
# Command with argument
args::option "deploy, d" "Deploy to server" "target"
# Multi-line description
args::option "--verbose" "Enable verbose output\nShows detailed progress"Add options to the immediate-option list. Immediate options trigger their handler function right after parsing and before returning control.
Default immediate options: -h, --help, -v, --version, --bash-completion.
args::immediate "--license"
# Now --license will trigger args::option::license if definedRegister a function to be called after options are extracted but before immediate options are dispatched. Hooks run in registration order.
my_pre_dispatch() {
echo "Options parsed"
}
args::on_options my_pre_dispatchUsed by args-completion.sh for auto-registration of completion scripts. Since immediate options (-h, --bash-completion) call exit, this is the only hook that fires in those cases.
Register a function to be called after every successful args::parse. Hooks run in registration order, after all option processing and command validation. Does not fire when immediate options exit early.
my_post_parse() {
echo "Parse complete: command=$args_command"
}
args::on_parse my_post_parsePrint a "try --help" hint. Used internally by error handlers. Checks for --help, then -h, then falls back to the program URL.
Parse command-line arguments. Must be called after all args::option registrations.
Internally:
- Builds
getoptoption strings from registered options. - Calls
getoptto normalize the arguments. - Iterates parsed arguments, populating
args_options. - Calls registered
args::on_optionshooks. - Handles immediate options (calls
args::option::NAMEfunctions, which typically exit). - Validates commands if any were registered.
- Calls registered
args::on_parsehooks.
After args::parse, these globals contain the results:
Associative array keyed by the primary option name (first name in the NAMES list). The value is the option's argument, or empty string for flags.
args::parse "$@"
# Check if an option was provided
if [[ -v args_options["--output"] ]]; then
echo "Output: ${args_options["--output"]}"
fi
# Shorthand via nameref
declare -n opt=args_options
if [[ -v opt["--dry-run"] ]]; then
echo "Dry run mode"
fiThe matched command name (primary name). Empty if no command was matched.
case "$args_command" in
build) do_build ;;
test) do_test ;;
esacArray of remaining positional arguments after options and commands are consumed.
# Iterate over remaining arguments
for arg in "${args_cleaned[@]}"; do
echo "Arg: $arg"
done
# Check if any remain
if [[ "${#args_cleaned[@]}" -gt 0 ]]; then
echo "Has positional arguments"
fiSet these after args::program and before args::parse:
| Variable | Default | Description |
|---|---|---|
args_program_required_command |
"yes" |
If "yes", error when no command is given (only when commands are defined) |
args_program_help_style |
"grid" |
Help layout: "grid" (side-by-side) or "list" (stacked) |
args_program_usage |
"" |
Custom usage text (overrides auto-generated) |
args_program_header |
"" |
Text between description and usage section |
args_program_footer |
"" |
Text at end of help (overrides URL-based footer) |
args_program_required_command="no" # commands are optional
args_program_help_style="list" # description below option name
# Custom usage (use box::make for multi-line)
source box.sh
args_program_usage="$(box::make \
"my-tool [options] build [target]" \
"my-tool [options] test [suite]")"Define these functions to customize error messages. If not defined, a default message is printed followed by args::try_help.
| Hook | Called when |
|---|---|
args::error::getopt |
getopt fails to parse |
args::error::unknown_option OPTION |
Unrecognized option |
args::error::no_command |
Required command missing |
args::error::unknown_command COMMAND |
Unrecognized command |
args::error::invalid_short_option NAME |
Malformed short option in definition |
args::error::invalid_long_option NAME |
Malformed long option in definition |
args::error::unknown_command() {
ansi::err "${RED}Unknown command: $1${RESET_ALL}"
args::try_help
}#!/usr/bin/env bash
set -euo pipefail
LIB_DIR="$(dirname "$(realpath "$0")")/../lib/options.bash"
source "${LIB_DIR}/args.sh"
source "${LIB_DIR}/args-help.sh"
source "${LIB_DIR}/args-version.sh"
args::program "deploy" "2.0.0" "Deploy application to servers"
args::option "-v, --version" "Show version"
args::option "-h, --help" "Show help"
args::option "-n, --dry-run" "Preview without executing"
args::option "--env, -e" "Target environment" "name"
args::option "--timeout, -t" "Timeout in seconds" "secs"
args::option "start" "Start the deployment"
args::option "stop" "Stop the deployment"
args::option "status" "Check deployment status"
args::parse "$@"
declare -n opt=args_options
env="${opt["--env"]:-production}"
timeout="${opt["--timeout"]:-30}"
case "$args_command" in
start)
echo "Deploying to ${env} (timeout: ${timeout}s)..."
if [[ -v opt["--dry-run"] ]]; then
echo "(dry run — no changes made)"
fi
;;
stop)
echo "Stopping deployment on ${env}..."
;;
status)
echo "Checking status on ${env}..."
;;
esac