When writing shell scripts, handling command-line options effectively is essential. A common scenario developers encounter is needing to process the same command-line option multiple times within a script. While many basic tutorials cover simple option parsing, fewer resources explain clearly how to handle repeated options. This tutorial will demonstrate how to process a repeated option multiple times in your shell script clearly and effectively.

We’ll cover the most common tools (getopts, manual parsing, and advanced parsing using getopt) and provide practical examples and insights to help you choose the best solution for your situation.


Why Is Handling Repeated Options Important?

Repeated options allow users to pass multiple values for the same option, making scripts flexible and user-friendly. For example, consider a backup script that needs to accept multiple directories to back up, each specified by a separate -d option:

./backup.sh -d /etc -d /home -d /var/log

Supporting repeated options is crucial for scripts designed for flexible, real-world use.


Method 1: Using getopts to Handle Repeated Options

The built-in getopts command is one of the most common and straightforward ways to handle command-line options. However, getopts doesn’t natively support repeated options. To use it effectively, you must manually store repeated arguments in an array.

Here’s a practical example demonstrating how you can handle repeated options with getopts:

Example Script:

#!/bin/bash

# Initialize an empty array to store directories
declare -a directories

# Parse command-line options using getopts
while getopts ":d:" opt; do
  case ${opt} in
    d )
      directories+=("$OPTARG")
      ;;
    \? )
      echo "Invalid option: -$OPTARG" >&2
      exit 1
      ;;
    : )
      echo "Option -$OPTARG requires an argument." >&2
      exit 1
      ;;
  esac
done

# Example of using the collected values
echo "Directories to back up:"
for dir in "${directories[@]}"; do
  echo " - $dir"
done

Explanation:

  • We initialize an empty bash array named directories.
  • Each time the -d option is encountered, its argument ($OPTARG) is pushed into the directories array.
  • After parsing, we loop through the array and process each directory individually.

Running the Script:

./backup.sh -d /etc -d /home -d /var/log

Output:

Directories to back up:
 - /etc
 - /home
 - /var/log

Method 2: Manual Parsing for Repeated Options

In some environments or scripts, you may prefer manual parsing due to its simplicity and flexibility. Here’s how you might do it:

Example Script:

#!/bin/bash

declare -a directories

# Loop through all arguments manually
while [[ $# -gt 0 ]]; do
  case "$1" in
    -d|--directory)
      if [[ -n "$2" && ! "$2" =~ ^- ]]; then
        directories+=("$2")
        shift 2
      else
        echo "Error: '-d|--directory' requires a non-empty option argument." >&2
        exit 1
      fi
      ;;
    *)
      echo "Invalid option: $1" >&2
      exit 1
      ;;
  esac
done

# Example processing
echo "Directories specified:"
for dir in "${directories[@]}"; do
  echo " - $dir"
done

Explanation:

  • We loop manually through arguments using a while loop and $# (argument count).
  • Each time we encounter the -d or --directory option, we store the next argument into our array.
  • We ensure the argument is not missing and not another option (not starting with a -).

Running the Script:

./backup.sh -d /etc --directory /home -d /var/log

Output:

Directories specified:
 - /etc
 - /home
 - /var/log

Method 3: Using Enhanced getopt for Repeated Options

The enhanced getopt command (GNU getopt) supports repeated options more naturally and handles short and long-form options. Here’s how you can leverage it:

Example Script:

#!/bin/bash

OPTIONS=$(getopt -o d: -l directory: -- "$@")
if [ $? -ne 0 ]; then
  echo "Incorrect options provided" >&2
  exit 1
fi

eval set -- "$OPTIONS"

declare -a directories

while true; do
  case "$1" in
    -d|--directory)
      directories+=("$2")
      shift 2
      ;;
    --)
      shift
      break
      ;;
    *)
      echo "Unexpected option: $1" >&2
      exit 1
      ;;
  esac
done

# Example processing
echo "Directories collected via getopt:"
for dir in "${directories[@]}"; do
  echo " - $dir"
done

Explanation:

  • getopt parses both short (-d) and long (--directory) options.
  • It rearranges the command-line arguments into a standardized format.
  • The script handles the -d/--directory option repeatedly, storing each occurrence in the array.

Running the Script:

./backup.sh -d /etc --directory /home -d /var/log

Output:

Directories collected via getopt:
 - /etc
 - /home
 - /var/log

Comparing the Methods

MethodAdvantagesDisadvantages
getoptsBuilt-in, simple syntaxLimited to single-letter options
Manual parsingMaximum flexibility and portabilityMore verbose, error-prone
Enhanced getoptSupports short/long options, easier parsingLess portable (GNU/Linux-specific)

Choose according to your script’s complexity, portability requirements, and readability preferences.


Conclusion

Processing repeated command-line options is a common, practical requirement in shell scripting. This tutorial presented three effective ways to handle repeated options: built-in getopts, manual parsing, and enhanced getopt. Each has its strengths and weaknesses, so choose the method best suited to your scripting needs and environment.

By clearly handling repeated options, your scripts become more robust, flexible, and user-friendly.


Sources and Further Reading


**