In older ksh or bash, the builtin command getopts
can usually only handle short option.
But newer Korn shell may be able to handle long options as show in an other article.
If you don't have the chance to get a recent shell, or if you want to write more portable script, this article will show how to handle GNU-style long options ( --
double hyphen) using getopts with few extra lines of scripting.
(foobar_any_getopts.sh zip file attachement in this article)
The script in this article has been tested ...
... on Oracle Linux 7
... with #!/bin/ksh and #!/bin/bash shebang
Getopts Basics
Syntax
getopts optstring name [args...]
optstring | specifies all the option characters that the script will recognize if a character is followed by : , then this option expects an argument if optstring start with : , it switches getopt in "error silent mode" |
name | variable which stores the current option character |
args | [optional] specifies arguments to parse (default value is $@) |
Special variables
Variable | Description |
OPTIND | store the index of the next argument to be processed Initially set to 1, and needs to be reset to 1 in order to parse anything else again |
OPTARG | stores the value of the option argument found by getopts |
How it works
getopts
is usually used in a while-loop and exit when it reaches the end of options.
while getopts ...; do
...
done
shift $((OPTIND-1))
getopts
recognizes the end of the options by any of the following conditions:
- an argument that doesn't start with
-
- the special argument
--
, marking the end of options - an error (for example, an unrecognized option letter)
Script example source code
foobar_any_getopts.sh is a script example to show how to use getopts with long options.
This script is also available for downloading at the end of the article.
#!/bin/ksh
#================================================================
#% SYNOPSIS
#+ ${SCRIPT_NAME} [-fb] [-B n] [-F[name]] files ...
#%
#% DESCRIPTION
#% This is a script example
#% to show how to use getopts with long options.
#%
#% OPTIONS
#% -f, --foo set foo setting (default)
#% -b, --bar set bar setting
#% -B n, --barfoo=n set barfoo to number n (default=2)
#% -F [name], --foobar=[name] set foobar and foobar_name
#% -h, --help print this help
#-
#- IMPLEMENTATION
#- version ${SCRIPT_NAME} (www.uxora.com) 0.0.2
#- author Michel VONGVILAY
#- copyright Copyright (c) http://www.uxora.com
#- license GNU General Public License
#================================================================
#== general functions ==#
usage() { printf "Usage: "; head -50 ${0} | grep "^#+" | sed -e "s/^#+[ ]*//g" -e "s/\${SCRIPT_NAME}/${SCRIPT_NAME}/g" ; }
usagefull() { head -50 ${0} | grep -e "^#[%+-]" | sed -e "s/^#[%+-]//g" -e "s/\${SCRIPT_NAME}/${SCRIPT_NAME}/g" ; }
#============================
# SET VARIABLES
#============================
unset SCRIPT_NAME SCRIPT_OPTS ARRAY_OPTS
#== general variables ==#
SCRIPT_NAME="$(basename ${0})"
OptFull=$@
OptNum=$#
#== program variables ==#
foo=1 bar=0
barfoo=2 foobar=0
foobar_name=
#============================
# OPTIONS WITH GETOPTS
#============================
#== set short options ==#
SCRIPT_OPTS=':fbF:B:-:h'
#== set long options associated with short one ==#
typeset -A ARRAY_OPTS
ARRAY_OPTS=(
[foo]=f
[bar]=b
[foobar]=F
[barfoo]=B
[help]=h
[man]=h
)
#== parse options ==#
while getopts ${SCRIPT_OPTS} OPTION ; do
#== translate long options to short ==#
if [[ "x$OPTION" == "x-" ]]; then
LONG_OPTION=$OPTARG
LONG_OPTARG=$(echo $LONG_OPTION | grep "=" | cut -d'=' -f2)
LONG_OPTIND=-1
[[ "x$LONG_OPTARG" = "x" ]] && LONG_OPTIND=$OPTIND || LONG_OPTION=$(echo $OPTARG | cut -d'=' -f1)
[[ $LONG_OPTIND -ne -1 ]] && eval LONG_OPTARG="\$$LONG_OPTIND"
OPTION=${ARRAY_OPTS[$LONG_OPTION]}
[[ "x$OPTION" = "x" ]] && OPTION="?" OPTARG="-$LONG_OPTION"
if [[ $( echo "${SCRIPT_OPTS}" | grep -c "${OPTION}:" ) -eq 1 ]]; then
if [[ "x${LONG_OPTARG}" = "x" ]] || [[ "${LONG_OPTARG}" = -* ]]; then
OPTION=":" OPTARG="-$LONG_OPTION"
else
OPTARG="$LONG_OPTARG";
if [[ $LONG_OPTIND -ne -1 ]]; then
[[ $OPTIND -le $Optnum ]] && OPTIND=$(( $OPTIND+1 ))
shift $OPTIND
OPTIND=1
fi
fi
fi
fi
#== options follow by another option instead of argument ==#
if [[ "x${OPTION}" != "x:" ]] && [[ "x${OPTION}" != "x?" ]] && [[ "${OPTARG}" = -* ]]; then
OPTARG="$OPTION" OPTION=":"
fi
#== manage options ==#
case "$OPTION" in
f ) foo=1 bar=0 ;;
b ) foo=0 bar=1 ;;
B ) barfoo=${OPTARG} ;;
F ) foobar=1 && foobar_name=${OPTARG} ;;
h ) usagefull && exit 0 ;;
: ) echo "${SCRIPT_NAME}: -$OPTARG: option requires an argument" >&2 && usage >&2 && exit 99 ;;
? ) echo "${SCRIPT_NAME}: -$OPTARG: unknown option" >&2 && usage >&2 && exit 99 ;;
esac
done
shift $((${OPTIND} - 1))
#============================
# MAIN SCRIPT
#============================
#== print variables ==#
echo foo=$foo bar=$bar
echo barfoo=$barfoo
echo foobar=$foobar foobar_name=$foobar_name
echo files=$@
Check how this script works
See how it manages options errors :
# Unknown option error
$ ./foobar_any_getopts.sh -x
<<--OUTPUT--
foobar_any_getopts.sh: -x: unknown option
Usage: foobar_any_getopts.sh [-fb] [-B n] [-F[name]] files ...
--OUTPUT--
# barfoo option without argument
# Expected argument error
$ ./foobar_any_getopts.sh -B
<<--OUTPUT--
foobar_any_getopts.sh: -B: option requires an argument
Usage: foobar_any_getopts.sh [-fb] [-B n] [-F[name]] files ...
--OUTPUT--
Now see it working without errors :
# foobar short option and barfoo short option
$ ./foobar_any_getopts.sh -bF "Hello world" -B 6 file1 file2
<<--OUTPUT--
foo=0 bar=1
barfoo=6
foobar=1 foobar_name=Hello world
files=file1 file2
--OUTPUT--
# foobar short option and barfoo long option
$ ./foobar_any_getopts.sh --bar -F Hello --barfoo 6 file1 file2
<<--OUTPUT--
foo=0 bar=1
barfoo=6
foobar=1 foobar_name=Hello
files=file1 file2
--OUTPUT--
Better you test by yourself to see if it works for you.
Please leave comments and suggestions,
Michel.
Enjoyed this article? Please like it or share it.
Please connect with one of social login below (or fill up name and email)