NAME
Getopt::Long::Bash::Tutorial - Getting started with getoptlong.sh
INTRODUCTION
getoptlong.sh is a Bash library that brings Perl's Getopt::Long-style option parsing to shell scripts. If you've ever wished for cleaner, more powerful option handling in Bash, this tutorial will get you started.
YOUR FIRST SCRIPT
Let's create a simple script that accepts a few options:
#!/usr/bin/env bash
declare -A OPTS=(
[verbose|v]=
[output|o:]=/dev/stdout
)
. getoptlong.sh OPTS "$@"
echo "Verbose: $verbose"
echo "Output: $output"
echo "Remaining args: $@"
Save this as myscript.sh, make it executable, and try:
$ ./myscript.sh -v -o result.txt file1.txt file2.txt
Verbose: 1
Output: result.txt
Remaining args: file1.txt file2.txt
That's it! With just a few lines, you have:
Short (
-v) and long (--verbose) option supportOptions with arguments (
-o result.txt)Default values (
/dev/stdout)Automatic separation of options from arguments
UNDERSTANDING THE BASICS
The Options Array
Options are defined in a Bash associative array using declare -A. Each key-value pair defines one option: the key specifies the option name and type, the value sets the default.
declare -A OPTS=(
[name|alias TYPE]=DEFAULT
)
Option Names and Aliases
The first name is the "long" option (--verbose), aliases after | are typically short options (-v). Hyphens in names become underscores in variable names.
[verbose|v]= # --verbose or -v
[output|o]= # --output or -o
[dry-run|n]= # --dry-run or -n (variable becomes $dry_run)
Option Types
The type specifier after the option name determines how arguments are handled.
Flag Options (default)
A flag takes no argument. When specified, the variable is set to 1.
[verbose|v]= # -v, --verbose sets $verbose to 1
Required Argument (:)
The colon means the option requires an argument.
[output|o:]= # -o file.txt, --output=file.txt
Optional Argument (?)
Question mark means the argument is optional. The variable is set to empty string if no argument given, or to the argument value if provided. Use [[ -v var ]] to check if the option was specified.
[config|c?]= # --config or --config=myfile.conf
STEP-BY-STEP EXAMPLES
Example 1: A Simple Backup Script
#!/usr/bin/env bash
declare -A OPTS=(
[verbose|v # Show detailed output]=
[dry-run|n # Show what would be done]=
[dest|d: # Destination directory]=/backup
)
. getoptlong.sh OPTS "$@"
# Check for source files
if (( $# == 0 )); then
echo "Usage: $0 [options] <files...>" >&2
exit 1
fi
for file in "$@"; do
[[ -n "$verbose" ]] && echo "Backing up: $file"
if [[ -z "$dry_run" ]]; then
cp "$file" "$dest/"
else
echo "[DRY RUN] Would copy $file to $dest/"
fi
done
Try it:
$ ./backup.sh -v -n file1.txt file2.txt
Backing up: file1.txt
[DRY RUN] Would copy file1.txt to /backup/
Backing up: file2.txt
[DRY RUN] Would copy file2.txt to /backup/
Example 2: Using Arrays for Multiple Values
Sometimes you need to accept the same option multiple times:
#!/usr/bin/env bash
declare -A OPTS=(
[include|I@]=
[exclude|X@]=
)
. getoptlong.sh OPTS "$@"
echo "Include paths:"
for path in "${include[@]}"; do
echo " - $path"
done
echo "Exclude patterns:"
for pattern in "${exclude[@]}"; do
echo " - $pattern"
done
The @ suffix creates an array. Try it:
$ ./example.sh -I /usr/lib -I /opt/lib -X "*.bak" -X "*.tmp"
Include paths:
- /usr/lib
- /opt/lib
Exclude patterns:
- *.bak
- *.tmp
Example 3: Using Hashes for Key-Value Pairs
For configuration-style options:
#!/usr/bin/env bash
declare -A OPTS=(
[define|D%]=
)
. getoptlong.sh OPTS "$@"
echo "Defined variables:"
for key in "${!define[@]}"; do
echo " $key = ${define[$key]}"
done
The % suffix creates an associative array:
$ ./example.sh -D NAME=MyApp -D VERSION=1.0 -D DEBUG
Defined variables:
NAME = MyApp
VERSION = 1.0
DEBUG = 1
Note: -D DEBUG without a value is treated as DEBUG=1.
Example 4: Verbosity Levels
Use + for incremental flags:
#!/usr/bin/env bash
declare -A OPTS=(
[verbose|v+]=0
)
. getoptlong.sh OPTS "$@"
if (( verbose >= 3 )); then
echo "Maximum verbosity: showing everything"
elif (( verbose >= 2 )); then
echo "High verbosity: showing details"
elif (( verbose >= 1 )); then
echo "Verbose mode on"
else
echo "Quiet mode"
fi
Try different verbosity levels:
$ ./example.sh
Quiet mode
$ ./example.sh -v
Verbose mode on
$ ./example.sh -vvv
Maximum verbosity: showing everything
HELP MESSAGES
getoptlong.sh automatically provides --help / -h. Text after # in the option definition becomes the help description. Use &USAGE to set the synopsis line.
#!/usr/bin/env bash
declare -A OPTS=(
[input|i: # Input file to process]=
[output|o: # Output file (default: stdout)]=/dev/stdout
[verbose|v # Enable verbose output]=
[&USAGE]="Usage: $(basename $0) [options] <files...>"
)
. getoptlong.sh OPTS "$@"
Output:
$ ./example.sh --help
Usage: example.sh [options] <files...>
Options:
-h, --help show help
-i, --input <value> Input file to process
-o, --output <value> Output file (default: stdout) (default: /dev/stdout)
-v, --verbose Enable verbose output
VALIDATION
Add validation to ensure correct input. Use =i for integers, =f for floats, or =(regex) for pattern matching:
[count|c:=i]=1 # Must be an integer
[ratio|r:=f]=0.5 # Must be a number
[mode|m:=(^(fast|slow|auto)$)]=auto # Must match regex
Invalid input produces an error:
$ ./example.sh --count abc
getoptlong: option '--count' requires integer value
MULTI-STEP USAGE
The one-liner form is convenient, but for more control use the explicit multi-step form. This is useful when you need to handle parse errors yourself, configure advanced options, or process subcommands.
#!/usr/bin/env bash
. getoptlong.sh
declare -A OPTS=(
[verbose|v]=
[output|o:]=
)
getoptlong init OPTS
getoptlong parse "$@" || exit 1
eval "$(getoptlong set)"
# Now use $verbose, $output, and "$@"
NEXT STEPS
Now that you understand the basics, read getoptlong for the complete reference manual and look at the ex/ directory for more examples including subcommand handling (ex/subcmd.sh).
SEE ALSO
getoptlong - Complete reference manual
https://github.com/tecolicom/getoptlong
AUTHOR
Kazumasa Utashiro
COPYRIGHT
Copyright 2025 Kazumasa Utashiro
LICENSE
MIT License