The getopts builtin parses command line arguments, thereby making it easier to write programs that follow the Linux argument conventions. The syntax for getopts is getopts optstring varname [arg...]
where optstring is a list of the valid option letters, varname is the variable that receives the options one at a time, and arg is the optional list of parameters to be processed. If arg is not present, getopts processes the command line arguments. If optstring starts with a colon (:), the script takes care of generating error messages; otherwise, getopts generates error messages.
The getopts builtin uses the OPTIND (option index) and OPTARG (option argument) variables to store option-related values. When a shell script starts, the value of OPTIND is 1. Each time getopts locates an argument, it increments OPTIND to the index of the next option to be processed. If the option takes an argument, bash assigns the value of the argument to OPTARG.
To indicate that an option takes an argument, follow the corresponding letter in optstring with a colon (:). The option string dxo:lt:r indicates that getopts should search for -d, -x, -o, -l, -t, and -r options and that the -o and -t options take arguments.
Using getopts as the test-command in a while control structure allows you to loop over the options one at a time. The getopts builtin checks the option list for options that are in optstring. Each time through the loop, getopts stores the option letter it finds in varname.
Suppose that you want to write a program that can take three options:
1. A -b option indicates that the program should ignore whitespace at the start of input lines.
2. A -t option followed by the name of a directory indicates that the program should use that directory for temporary files. Otherwise, it should use /tmp.
3. A -u option indicates that the program should translate all its output to uppercase.
In addition, the program should ignore all other options and end option processing when it encounters two hyphens (—).
The problem is to write the portion of the program that determines which options the user has supplied. The following solution does not use getopts:
SKIPBLANKS= TMPDIR=/tmp CASE=1ower while [[ "$1" = -* ]] # [[ = ]] does pattern match do case $1 in
-b) SKIPBLANKS=TRUE ;; -t) if [ -d "$2" ] then
TMPDIR=$2 shift else echo "$0: -t takes a directory argument." >&2 exit 1 fi ;;
*) echo "$0: Invalid option $1 ignored." >&2 ;; esac shift done
This program fragment uses a loop to check and shift arguments while the argument is not —. As long as the argument is not two hyphens, the program continues to loop through a case statement that checks for possible options. The — case label breaks out of the while loop. The * case label recognizes any option; it appears as the last case label to catch any unknown options, displays an error message, and allows processing to continue. On each pass through the loop, the program does a shift to get to the next argument. If an option takes an argument, the program does an extra shift to get past that argument.
The following program fragment processes the same options, but uses getopts:
SKIPBLANKS= TMPDIR=/tmp CASE=1ower while getopts :bt:u arg do case $arg in b) SKIPBLANKS=TRUE ;;
TMPDIR=$OPTARG else echo "$0: $OPTARG is not a directory." >&2 exit 1 fi ;;
:) echo "$0: Must supply an argument to -$OPTARG." >&2
\?) echo "Invalid option -$OPTARG ignored." >&2 ;; esac done
In this version of the code, the while structure evaluates the getopts builtin each time it comes to the top of the loop. The getopts builtin uses the OPTIND variable to keep track of the index of the argument it is to process the next time it is called. There is no need to call shift in this example.
In the getopts version of the script the case patterns do not start with a hyphen because the value of arg is just the option letter (getopts strips off the hyphen). Also, getopts recognizes — as the end of the options, so you do not have to specify it explicitly as in the case statement in the first example.
Because you tell getopts which options are valid and which require arguments, it can detect errors in the command line and handle them in two ways. This example uses a leading colon in optstring to specify that you check for and handle errors in your code; when getopts finds an invalid option, it sets varname to ? and OPTARG to the option letter. When it finds an option that is missing an argument, getopts sets varname to : and OPTARG to the option lacking an argument.
The \? case pattern specifies the action to take when getopts detects an invalid option. The : case pattern specifies the action to take when getopts detects a missing option argument. In both cases getopts does not write any error message; it leaves that task to you.
If you omit the leading colon from optstring, both an invalid option and a missing option argument cause varname to be assigned the string ?. OPTARG is not set and getopts writes its own diagnostic message to standard error. Generally this method is less desirable because you have less control over what the user sees when an error is made.
Using getopts will not necessarily make your programs shorter. Its principal advantages are that it provides a uniform programming interface and it enforces standard option handling.
Was this article helpful?