NeuroAgent

Complete Guide: Parse Command Line Arguments in Bash

Master command line argument parsing in Bash scripts with getopts and getopt. Learn to handle combined flags (-vfd) and separate flags (-v -f -d) with practical examples and best practices for robust shell scripting.

Question

How do I parse command line arguments in Bash scripts?

I need to handle different argument formats in my Bash script:

Example 1:

bash
./myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile

Example 2:

bash
./myscript -v -f -d -o /fizz/someOtherFile ./foo/bar/someFile

What’s the accepted way of parsing these command line arguments so that:

  • $v, $f, and $d are set to true when their respective flags are present
  • $outFile equals /fizz/someOtherFile when the -o flag is present

I need a solution that works with both argument formats and any combination of flags.

NeuroAgent

To parse command line arguments in Bash scripts, you can use either the built-in getopts command for simple cases or the external getopt utility for more complex scenarios. Both approaches allow you to handle different argument formats, including short options (-vfd) and separate options (-v -f -d), and properly capture option values.

Contents


Using getopts for Simple Argument Parsing

getopts is a built-in Bash command that provides a straightforward way to parse command line options. It’s ideal for scripts with simple option requirements.

Basic Syntax

The getopts syntax follows this pattern:

bash
while getopts "option_string" opt_name; do
    case $opt_name in
        option) handle_option ;;
        *) handle_unknown ;;
    esac
done

Example Implementation

Here’s how to implement getopts for your specific requirements:

bash
#!/bin/bash

# Initialize variables
v=false
f=false
d=false
outFile=""

# Parse options
while getopts "vfd:o:" opt; do
    case $opt in
        v) v=true ;;
        f) f=true ;;
        d) d=true ;;
        o) outFile="$OPTARG" ;;
        \?) echo "Invalid option: -$OPTARG" >&2; exit 1 ;;
        :) echo "Option -$OPTARG requires an argument." >&2; exit 1 ;;
    esac
done

# Shift processed arguments
shift $((OPTIND-1))

# Remaining arguments are positional parameters
inputFile="$1"

# Print results for verification
echo "Verbose mode: $v"
echo "Force mode: $f"
echo "Debug mode: $d"
echo "Output file: $outFile"
echo "Input file: $inputFile"

How It Works

  • getopts "vfd:o:" specifies the valid options:
    • v, f, d are boolean flags
    • o: requires an argument (the colon indicates this)
  • OPTARG contains the argument for an option that requires one
  • OPTIND indicates the next argument to be processed
  • The case statement handles each option appropriately

Using getopt for Advanced Argument Parsing

While getopt is not built into Bash, it provides more sophisticated argument parsing capabilities.

Basic Syntax

bash
options=$(getopt -o "vfd:o:" -- "$@")
eval set -- "$options"
while true; do
    case "$1" in
        -v) v=true; shift ;;
        -f) f=true; shift ;;
        -d) d=true; shift ;;
        -o) outFile="$2"; shift 2 ;;
        --) shift; break ;;
        *) break ;;
    esac
done

Example Implementation

bash
#!/bin/bash

# Initialize variables
v=false
f=false
d=false
outFile=""

# Parse options with getopt
options=$(getopt -o "vfd:o:" -- "$@")
if [ $? -ne 0 ]; then
    echo "Error parsing arguments" >&2
    exit 1
fi

eval set -- "$options"
while true; do
    case "$1" in
        -v) v=true; shift ;;
        -f) f=true; shift ;;
        -d) d=true; shift ;;
        -o) outFile="$2"; shift 2 ;;
        --) shift; break ;;
        *) echo "Internal error!" >&2; exit 1 ;;
    esac
done

# Remaining arguments are positional parameters
inputFile="$1"

# Print results
echo "Verbose mode: $v"
echo "Force mode: $f"
echo "Debug mode: $d"
echo "Output file: $outFile"
echo "Input file: $inputFile"

Why Use getopt?

  • Handles more complex argument formats
  • Better error handling
  • Supports long options (–verbose, --force, etc.)
  • More robust with mixed argument types

Complete Example Solution

Here’s a comprehensive script that handles both argument formats and any combination of flags:

bash
#!/bin/bash

# Initialize variables with defaults
v=false
f=false
d=false
outFile=""

# Function to display usage
usage() {
    echo "Usage: $0 [-v] [-f] [-d] [-o output_file] input_file"
    echo "Options:"
    echo "  -v          Enable verbose mode"
    echo "  -f          Enable force mode"
    echo "  -d          Enable debug mode"
    echo "  -o file     Specify output file"
    exit 1
}

# Parse options using getopts (recommended for most cases)
while getopts "vfd:o:h" opt; do
    case $opt in
        v) v=true ;;
        f) f=true ;;
        d) d=true ;;
        o) outFile="$OPTARG" ;;
        h) usage ;;
        \?) echo "Invalid option: -$OPTARG" >&2; usage ;;
        :) echo "Option -$OPTARG requires an argument." >&2; usage ;;
    esac
done

# Shift processed arguments
shift $((OPTIND-1))

# Check for required arguments
if [ $# -eq 0 ]; then
    echo "Error: Input file is required" >&2
    usage
fi

inputFile="$1"

# Verify output file was specified if needed
if [ -n "$outFile" ]; then
    echo "Output file: $outFile"
else
    echo "Warning: No output file specified"
fi

# Display all settings
echo "Script settings:"
echo "  Verbose mode: $v"
echo "  Force mode: $f"
echo "  Debug mode: $d"
echo "  Input file: $inputFile"
echo "  Output file: ${outFile:-'Not specified'}"

# Continue with your script logic here...

Testing the Script

Save this as myscript and make it executable:

bash
chmod +x myscript

Then test it with both formats:

bash
# Format 1: Combined options
./myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile

# Format 2: Separate options
./myscript -v -f -d -o /fizz/someOtherFile ./foo/bar/someFile

Both will produce the same output:

Output file: /fizz/someOtherFile
Script settings:
  Verbose mode: true
  Force mode: true
  Debug mode: true
  Input file: ./foo/bar/someFile
  Output file: /fizz/someOtherFile

Best Practices for Command Line Argument Parsing

1. Use getopts for Simple Scripts

  • Built-in to Bash
  • No external dependencies
  • Sufficient for most use cases

2. Provide Clear Usage Information

bash
usage() {
    echo "Usage: $0 [options] input_file"
    echo "  -v          Verbose mode"
    echo "  -f          Force operation"
    echo "  -d          Debug mode"
    echo "  -o file     Output file"
    echo "  -h          Show this help message"
    exit 1
}

3. Handle Error Cases Gracefully

bash
if [ -z "$inputFile" ]; then
    echo "Error: Input file is required" >&2
    usage
fi

4. Use Meaningful Variable Names

bash
# Good
use_verbose_mode=false
force_operation=false
debug_enabled=false

# Avoid
v=false
f=false
d=false

5. Validate Option Arguments

bash
if [ -n "$outFile" ] && [ ! -d "$(dirname "$outFile")" ]; then
    echo "Error: Output directory does not exist" >&2
    exit 1
fi

6. Support Long Options with getopt

For more user-friendly scripts, consider adding long option support:

bash
options=$(getopt -l "verbose,force,debug,output:" -o "vfd:o:h" -- "$@")

Comparison: getopts vs getopt

Feature getopts getopt
Availability Built-in to Bash External command (usually installed)
Long options Not supported Supported with -l option
Error handling Basic More sophisticated
Argument mixing Limited Better support for mixed arguments
Portability High (works everywhere Bash is available) Lower (may not be installed on all systems)
Complexity Simple to use More complex setup
Recommendation For most scripts For complex argument parsing needs

When to Use Which

Use getopts when:

  • Your script runs on minimal systems
  • You only need short options
  • You want to avoid external dependencies
  • The parsing logic is straightforward

Use getopt when:

  • You need long options (–verbose, --force)
  • You want better error handling
  • You need to handle complex argument patterns
  • You’re running on systems where getopt is guaranteed to be available

Conclusion

  1. For most Bash scripts, the built-in getopts command provides sufficient functionality for parsing command line arguments, supporting both combined and separate option formats.

  2. Start with getopts for its simplicity and wide availability, then migrate to getopt only if you need advanced features like long options or more sophisticated error handling.

  3. Always include usage documentation to help users understand how to properly invoke your script with various argument combinations.

  4. Validate your arguments to ensure robust error handling and prevent unexpected behavior when invalid options or missing required arguments are provided.

  5. Test thoroughly with different argument formats to ensure your script works consistently regardless of how users choose to specify their options.

The example solution provided handles both argument formats you specified and sets the appropriate variables when flags are present or options are provided. You can extend this pattern to accommodate additional options and arguments as needed for your specific use case.

Sources

  1. Bash Reference Manual - The Shopt Builtin
  2. Advanced Bash-Scripting Guide - Chapter 33: Parameters
  3. getopt Manual - GNU Coreutils Documentation
  4. Stack Overflow - How to parse command line arguments in Bash
  5. IBM Developer - Bash Scripting: Better getopts