---
title: My bash template for option parsing
tags: [getopts, bash, template, bashisms, shell]
updated: 2021-12-29 18:23:28
description: a template for option parsing in Bash based on getopt
---
Hello again,
in the past years I have written a lot of scripts which rely on
`getopt` , a program that parses command line options (both short and long) and
arguments.
<!-- more -->
Getopt is part of the [util-linux ](https://www.kernel.org/pub/linux/utils/util-linux/ ) package.
Please, [don't confuse getopt with getopts ](https://unix.stackexchange.com/questions/62950/getopt-getopts-or-manual-parsing-what-to-use-when-i-want-to-support-both-shor )
; they are quite different.
See [this ](https://www.mariusvw.com/2013/02/24/bash-getopt-versus-getopts/ )
and [this ](http://abhipandey.com/2016/03/getopt-vs-getopts/ ) for a comparison
between the two.
*Please note that the script and the documentation reported here, except for
the introduction and the "Reason" paragraph, is outdated. For this
reason you should refer to the [repository ](https://software.franco.net.eu.org/frnmst-archives/fbopt ).*
*Please note that the documentation and source code reported here are old.
Refer to the [fbopt ](https://software.franco.net.eu.org/frnmst-archives/fbopt ) repository.*
## Reason
To avoid reinventing the wheel and confusion, I have written some notes and a
dummy script that will serve as template. The script is called `fbopt` , i.e:
*Franco Bash Option Parsing Template*. The purpose of this is to change
the current implementations of the option parsers already present in
some scripts I have written, as well as use it for new ones.
## Usage
You can for example adapt and include the template as a separate part of your
program by using `source ./fbopt` or `. ./fbopt` from the main
script.
## Bashisms
[Bashisms ](https://mywiki.wooledge.org/Bashism ) are Bash specific syntax
elements. You will find at least the following bashisms in the template
| Bashism | Example |
|---------|---------|
| local variables | `local variable='value'` |
| arrays | `"${array[@]}"` |
| indirect variables | `"${!variable}"` |
## Conventions
These elements should be common sense and not be specific to `fbopt`
| Convention | Example | Notes |
|------------|---------|-------|
| constants are enclosed within single quotes | `'constant'` | elements within single quotes (only) are not interpreted by the shell |
| variables enclosed within double quotes | `"${variable}"` | double quotes serve as a delimiter between multiple variable names if these are consecutive. Every variable between the quotes is interpolated |
| variables use the curly braces notation | `"${variable}"` | curly braces serve as a delimiter between multiple variable names if these are consecutive |
| variables not enclosed within double quotes | `${?}` or `for v in ${values}; do echo "${v}"; done` | the only variables allowed without double quotes are integers (such as return values) and loop iterators (because these won't work otherwise) |
## Dependencies and compliancy
The template is known to work with the following packages
| Package | Version command |
|---------|-----------------|
| [GNU Bash ](http://www.gnu.org/software/bash/bash.html ) | `$ bash --version` |
| [getopt ](https://www.kernel.org/pub/linux/utils/util-linux/ ) | `$ getopt --version` |
`fbopt` compatibility:
| Package | Package version | fbopt version |
|---------|-----------------|---------------|
| GNU Bash | `GNU bash, version 4.4.23(1)-release (x86_64-unknown-linux-gnu)` | `0.1` , `0.2` , `0.3` |
| getopt | `getopt from util-linux 2.33` | `0.1` , `0.2` , `0.3` |
## The template
To allow inclusion in any project the template is released under the [CC0 1.0 license ](https://creativecommons.org/publicdomain/zero/1.0/legalcode.txt ).
What follows is `fbopt` version `0.3` .
```shell
#!/bin/bash
#
# fbopt version 0.3
#
# Written in 2018 by Franco Masotti/frnmst <franco.masotti@student.unife.it>
#
# To the extent possible under law, the author(s) have dedicated all
# copyright and related and neighboring rights to this software to the public
# domain worldwide. This software is distributed without any warranty.
#
# You should have received a copy of the CC0 Public Domain Dedication along
# with this software. If not, see
# <http://creativecommons.org/publicdomain/zero/1.0/>.
#
#
# See also https://blog.franco.net.eu.org/notes/my-bash-option-parsing-template.html
#
show_help()
{
cat < < -EOF
Usage: < program_name > [OPTION] ARGUMENT
< Description >
Mandatory arguments to long options are mandatory for short options too.
Options:
-a, --flag-a=FLAGA set a
-b, --flag-b=FLAGB set b
-c enable c
-d enable d
-e, --flag-e=FLAGE set e
-f[=FLAGF] enable and or set f
with an optional argument
-h, --help print this help
--print-flags print the enabled options. This can also
be used to print the default options
Exit status:
0 if OK,
1 if an error occurred.
< License header >
< Copyright >
EOF
}
# A function that prints the variable name and value of all
# the flags enabled by the user. This is useful to check that
# all the flags are correct, as kind of a dry run.
show_flags()
{
local flags="${*}"
for flag in ${flags}; do
printf "%s='%s'\n" "${flag}" "${!flag}"
done
}
getopt_error()
{
local program_name="${0}"
printf "%s\n" "Try '"${program_name}" --help' for more information"
} 1>& 2 2>& -
main()
{
# Create a new array from the reference of the input one.
# See https://stackoverflow.com/questions/1063347/passing-arrays-as-parameters-in-bash
# See some comments below.
declare -a argc=("${!1}")
# Set the options. flag_f has an optional argument.
local getopt_short_options='a:b:c:def::hi'
local getopt_long_options='flag-a:,flag-b:,flag-e,print-flags,help,'
# Set the default values for the flags.
local flag_a=''
local flag_b=''
local flag_c=''
local flag_d='false'
local flag_e='false'
local flag_f='false'
local program_name="${0}"
opts="$(getopt \
--name "${program_name}" \
--shell bash \
--options "${getopt_short_options}" \
--longoptions "${getopt_long_options}" \
-- \
"${argc[@]}")"
getopt_retval=${?}
# Check that getopt works and that some kind of argument
# is passed to the script. This is "quotation hell".
a="'"${argc[@]}"'"
{ [ ${getopt_retval} -ne 0 ] || [ -z "${a}" ]; } & & getopt_error & & return 1
eval set -- "${opts}"
# Option parsing.
while [ "${1}" != '--' ]; do
case "${1}" in
-a | --flag-a ) flag_a="${2}";
shift 1 ;;
--flag-b ) flag_b="${2}";
shift 1 ;;
-c ) flag_c="${2}";
shift 1 ;;
-d ) flag_d='true' ;;
-e | --flag-e ) flag_e='true' ;;
-f ) flag_f="${2}";
shift 1 ;;
-h | --help ) help='true' ;;
--print-flags ) print_flags='true' ;;
esac
# Iterate through all arguments.
shift 1
done
shift 1
# Everything else after '--' is an argument.
argc="${*}"
# Check that the flags that must be non empty are actually not empty.
# A user might infact circumvent getopt's mechanisms like this
# ./program -flag ''
# This can also be done inside the option parser loop but to avoid nestings
# I prefer it done here.
{ [ -z "${flag_a}" ] \
|| [ -z "${flag_b}" ]; } \
& & getopt_error & & return 1
[ "${print_flags}" = 'true' ] \
& & show_flags \
'flag_a' \
'flag_b' \
'flag_c' \
'flag_d' \
'flag_e' \
'flag_f' \
& & return 0
[ "${help}" = 'true' ] & & show_help & & return 0
# Override values of optional parameters.
[ -z "${flag_f}" ] & & flag_f='true'
# From now on you should call a function or an external program
# using the values of the flag variables.
[ "${flag_a}" = 'alpha' ] & & do_something_alpha & & return ${?}
[ "${flag_a}" = 'a' ] & & do_something_a & & return ${?}
}
# Test dependencies and versions.
# getopt must return 4 to be fully compatible. See getopt's manual.
which bash getopt 1>/dev/null 2>/dev/null & & { getopt -T; [ ${?} -eq 4 ]; }
# Get and pass argc to the main function.
# All this work with an array must be done to preserve
# quotations for arguments that have whitespaces.
# See https://lists.debian.org/debian-user/2007/12/msg01244.html
declare -a opts=()
for opt in "${@}"; do
opts=("${opts[@]}" "${opt}")
done
main 'opts[@]'
```
~
Enjoy :)