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 support

  • Options 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

Getopt::Long::Bash

https://github.com/tecolicom/getoptlong

AUTHOR

Kazumasa Utashiro

COPYRIGHT

Copyright 2025 Kazumasa Utashiro

LICENSE

MIT License