Home Bash scripting Bash Scripting – Parse Arguments In Bash Scripts Using getopts

Bash Scripting – Parse Arguments In Bash Scripts Using getopts

By Karthick
Published: Last Updated on 11.6K views

Passing arguments to a program is one of the common operations across any language you use. Similarly, in this article, we are going to see how to parse arguments in bash scripts using getopts bash built-in function in Linux.

Introduction

Every command that we run in the terminal has an argument associated with it. For example, you can take the most basic command in Linux named df which displays file system disk space usage. It accepts arguments/flags like -h, -i, --version, etc.

Similar to this, when you are creating shell scripts depending upon the use case, you may have to process the script based on the argument passed.

There are two ways to parse arguments passed to the script in bash.

One is writing the logic to parse the arguments manually using the special variables $@, $1, $2 … $N. Another way would be to use getopts.

Getopts is a POSIX compatible bash built-in function that accepts short arguments like -h, -v, -b, etc. You cannot pass long arguments like --help, --version. If you want to parse long options there is another utility called getopt which is an external program and not bash built-in.

There are three possible scenarios I can think of when working with flags/arguments.

  • The script which runs even when no argument or flags are passed.
  • The script that accepts flags but with no arguments.
  • The script that accepts flags and associated arguments for the flag.

You will learn how to create a bash script satisfying the above scenarios in the upcoming sections.

Getting Help

You can run the following help command to access the getopts help section.

$ getopts -help

Core Construct Of Getopts

There are four important terms you should know about in order to work with getopts.

getopts: getopts optstring name [arg …]
  1. OPTSTRING - The list of characters that are recognized as arguments. Example: -h, -v.
  2. OPTNAME - The parsed argument from $@ will be stored in the OPTNAME variable. You can use any name as optname.
  3. OPTARG - If there are additional arguments passed then that is stored in the OPTARG variable.
  4. OPTIND - Index pointing to the next argument to be processed.

When you pass the arguments to the script, they will be stored in variable $@. Getopts will get the list of arguments from $@ use OPTIND and parse it.

You should specify the list of recognized arguments in OPTSTRING. While loop is used to iterate over the list of passed arguments and using OPTIND, getopts will get the argument and store it in OPTNAME.

The case statement is used to check if the pattern matches with OPTNAME and run its corresponding statement.

I am going to run the below code throughout the article. This is a simple code to understand. First I have created a function named "help" where there is a usage syntax for my script.

I am using three arguments in OPTSTRING ; "-s, -T, -h". Variable named ARG is the OPTNAME used in the below code. The parsed argument will be stored in $ARG and using the case statement it will try to find if the pattern matches with the argument in the ARG variable.

Following script will accept flags like -s, -h, -T and you can also combine flags like -shT.

#!/bin/bash

function help(){
    echo "USAGE: args.sh -s, -T <arg>, -h"
}

while getopts ":sT:h" ARG; do
  case "$ARG" in 
    s) echo "Running -s flag" ;;
    T) echo "Running -T flag"
       echo "Argument passed is $OPTARG" ;;
    h) help ;;
    :​) echo "argument missing" ;;
    \?) echo "Something is wrong" ;;
  esac
done

shift "$((OPTIND-1))"

The functionality of the above code will be explained in detail in the upcoming section.

No Argument Passed

The default behavior of getopts is if no argument is passed, it will not throw an error message and will exit with return code zero.

Default Behavior
Default Behavior

In some cases, the script should not run when no argument is passed. In that case, you can use conditional statements to check the length of arguments passed ($@) is zero or not. If no arguments are passed, the script will fail.

Take a look at the below code. I am storing the passed arguments to variable PASSED_ARGS and checking the length of it. If the length of PASSED_ARGS is not zero, then while loop with getopts will run, else help function will run and the script will exit with return code 1.

PASSED_AGS=$@
if [[ ${#PASSED_ARGS} -ne 0 ]]
then
  while getopts ":sT:h" ARG; do
  case "$ARG" in 
    s) echo "Running -s flag" ;;
    T) echo "Running -T flag"
       echo "Argument passed is $OPTARG" ;;
    h) help ;;
    :​) echo "argument missing for $ARG" ;;
    \?) echo "Something is wrong" ;;
  esac
  done
else
  help
  exit 1
fi

shift "$((OPTIND-1))"
Error On No Argument
Error On No Argument

You can also use the following one-liner to evaluate the arguments passed.

[[ ${#PASSED_ARGS} -ne 0 ]] && echo "Arguments stored in \$@ = $@" || { echo "No argument passed"; help; exit 1; }

Arguments With Flags And Its Supported Arguments

You can either just pass flags like -h or -s to the script or flags and associated argument like -T to it. You should add a colon (:) after the flag identifier (-T) to make the flag accept the argument.

In the below code, you can see I have added colon (:) after the identifier T. It means when I pass the -T flag to the script, I should pass an additional argument with it.

while getopts ":sT:h" ARG; do

When an argument is passed to the flag it will be stored in the variable $OPTARG. You have to write logic to capture the variable and process it accordingly. Copy and run the same code from the previous section.

T) echo "Running -T flag"
   echo "Argument passed is $OPTARG" ;;
Arguments With Flags
Arguments With Flags

You can also combine and run multiple arguments at a time.

Running With Multiple Arguments
Running With Multiple Arguments

Handling Errors

There may be situations where arguments are passed wrongly and the script should throw some errors.

By default, getopts will throw an error message when a flag passed is not in OPTSTRING or if you fail to pass additional arguments to the flag. Adding a colon to the start of OPTSTRING will suppress the default error messages.

Let’s remove the colon and run the script again. I am passing -x as the first argument which is not present in OPTSTRING. Secondly, -T requires an additional argument which I failed to provide In both cases it throws me the error.

Default Error Messages
Default Error Messages

Now, you can suppress the default error messages and print your own error messages. Take a look at the below patterns from the case statement.

  • Colon (:) -> If no additional argument is passed OPTARG will be set to colon and you can write logic to print error messages.
  • \? -> When an argument that is not in OPTSTRING is passed, OPTNAME will be set to "?".
:​) echo "argument missing" ;;
\?) echo "Something is wrong" ;;
User Defined Error Messages
User Defined Error Messages

Use of Shift and OPTIND

If you take any existing scripts written using getopts you will see this statement after while loop.

shift "$((OPTIND-1))"

When the script is triggered, OPTIND is set to 1. The OPTIND points to the position of the next argument to be processed by getopts.

The above statement will remove all the options parsed by the getopts and $1 will not be set to the first non-optional argument passed to the script.

Conclusion

In this article, we have seen how to parse arguments in bash scripts using getopts function. Getopts only supports a short form of arguments and you cannot pass long arguments.

At first, it might seem tricky to work with getopts. But if you understand the core concept, then it gets easy.

You May Also Like

Leave a Comment

* By using this form you agree with the storage and handling of your data by this website.

This site uses Akismet to reduce spam. Learn how your comment data is processed.

This website uses cookies to improve your experience. By using this site, we will assume that you're OK with it. Accept Read More