1. Overview

As Linux users, we frequently use various command-line utilities and scripts. One of the common tasks while developing a script is to parse command-line options. These options can be short or long.

In this tutorial, we’ll use bash‘s getopts function and the getopt utility for parsing the command-line options.

2. Parsing Short Command-Line Options With getopts

In Bash, we can use both short and long command-line options. The short option starts with the single hyphen (-) character followed by a single alphanumeric character, whereas the long option starts with the double hyphen (–) characters followed by multiple characters.

We can instruct the ls command to display hidden files:

$ ls -a
.  ..  hello.txt
$ ls --all
.  ..  hello.txt

In the above example:

  • -a represents the short option
  • –all represents the long option

To parse short command-line options, we can use bash‘s built-in function getopts. It parses positional parameters as options.

2.1. getopts Syntax

The syntax of the getopts function is:

getopts optstring opt [arg ...]

In the above function:

  • optstring represents the supported options. The option expects an argument if there is a colon (:) after it. For instance, if option c expects an argument, then it would be represented as c: in the optstring
  • When an option has an associated argument, then getopts stores the argument as a string in the OPTARG shell variable. For instance, the argument passed to option c would be stored in the OPTARG variable.
  • opt contains the parsed option.

In the following sections, these concepts will become clear as we see them in action.

2.2. Example Usage in a Script

Let’s create a simple shell script to see its usage:

#!/bin/bash

while getopts 'abc:h' opt; do
  case "$opt" in
    a)
      echo "Processing option 'a'"
      ;;

    b)
      echo "Processing option 'b'"
      ;;

    c)
      arg="$OPTARG"
      echo "Processing option 'c' with '${OPTARG}' argument"
      ;;
   
    ?|h)
      echo "Usage: $(basename $0) [-a] [-b] [-c arg]"
      exit 1
      ;;
  esac
done
shift "$(($OPTIND -1))"

Let’s execute the script and observe the output:

$ chmod +x parse-command-line-args.sh

$ ./parse-command-line-args.sh -a 
Processing option 'a' 

$ ./parse-command-line-args.sh -c test-value
Processing option 'c' with 'test-value' argument 

$ ./parse-command-line-args.sh -ab 
Processing option 'a'
Processing option 'b'

2.3. Error Reporting in getopts

The getopts function reports an error in two scenarios —first, when an invalid option is used, and second, when an argument is not provided:

  • The opt variable contains a question mark (?) character if an invalid option is provided
  • The opt variable contains a colon (:) character if a required argument is not provided

We can catch both of these error scenarios and show an appropriate error message to the user. Let’s modify the script to handle these scenarios:

#!/bin/bash

while getopts ':abc:h' opt; do
  case "$opt" in
    a)
      echo "Processing option 'a'"
      ;;

    b)
      echo "Processing option 'b'"
      ;;

    c)
      arg="$OPTARG"
      echo "Processing option 'c' with '${OPTARG}' argument"
      ;;

    h)
      echo "Usage: $(basename $0) [-a] [-b] [-c arg]"
      exit 0
      ;;

    :)
      echo -e "option requires an argument.\nUsage: $(basename $0) [-a] [-b] [-c arg]"
      exit 1
      ;;

    ?)
      echo -e "Invalid command option.\nUsage: $(basename $0) [-a] [-b] [-c arg]"
      exit 1
      ;;
  esac
done
shift "$(($OPTIND -1))"

Now, we’ll execute the script and check the error messages:

$ ./parse-command-line-args.sh -c
option requires an argument.
Usage: parse-command-line-args.sh [-a] [-b] [-c arg]

$ ./parse-command-line-args.sh -e
Invalid command option.
Usage: parse-command-line-args.sh [-a] [-b] [-c arg]

Note that we’ve updated optstring as well. Now it starts with the colon(:) character, which suppresses the default error message. Apart from this, we can use the OPTERR environment variable to alter error reporting behavior. The getopts function disables error reporting when the OPTERR variable is set to zero.

3. Parsing Long Command-Line Options With getopt

Sometimes, it’s convenient to use long command-line options to improve readability.  However, getopts supports short command-line options only. We can use GNU’s getopt command to parse long command-line options:

#!/bin/bash

VALID_ARGS=$(getopt -o abg:d: --long alpha,beta,gamma:,delta: -- "[email protected]")
if [[ $? -ne 0 ]]; then
    exit 1;
fi

eval set -- "$VALID_ARGS"
while [ : ]; do
  case "$1" in
    -a | --alpha)
        echo "Processing 'alpha' option"
        shift
        ;;
    -b | --beta)
        echo "Processing 'beta' option"
        shift
        ;;
    -g | --gamma)
        echo "Processing 'gamma' option. Input argument is '$2'"
        shift 2
        ;;
    -d | --delta)
        echo "Processing 'delta' option. Input argument is '$2'"
        shift 2
        ;;
    --) shift; 
        break 
        ;;
  esac
done

In the above script:

  • -o option represents the short command-line options
  • –long option represents the long command-line options

Let’s execute the script and observe the output:

$ chmod +x parse-long-command-line-args.sh 
$ ./parse-long-command-line-args.sh -a
Processing 'alpha' option

$ ./parse-long-command-line-args.sh --alpha
Processing 'alpha' option

$ ./parse-long-command-line-args.sh --delta test-value
Processing 'delta' option. Input argument is 'test-value'

$ ./parse-long-command-line-args.sh -g test-value
Processing 'gamma' option. Input argument is 'test-value'

4. Conclusion

In this tutorial, we discussed two methods we can use to parse Linux command-line arguments. First, we discussed bash‘s built-in function getopts. Then, we discussed GNU’s getopt command. We can use these examples in day-to-day life to make our shell scripts more robust.

Authors Bottom

If you have a few years of experience in the Linux ecosystem, and you’re interested in sharing that experience with the community, have a look at our Contribution Guidelines.

Comments are closed on this article!