getopts

From English Wikipedia @ Freddythechick
getopts
Developer(s)Various open-source and commercial developers
Initial release1986; 39 years ago (1986)
Operating systemUnix, Unix-like, IBM i
TypeCommand

getopts is a built-in Unix shell command for parsing command-line arguments. It is designed to process command line arguments that follow the POSIX Utility Syntax Guidelines, based on the C interface of getopt.

The predecessor to <syntaxhighlight lang="text" class="" style="" inline="1">getopts</syntaxhighlight> was the external program <syntaxhighlight lang="text" class="" style="" inline="1">getopt</syntaxhighlight> by Unix System Laboratories.

History

The original <syntaxhighlight lang="text" class="" style="" inline="1">getopt</syntaxhighlight> had several problems: it could not handle whitespace or shell metacharacters in arguments, and there was no ability to disable the output of error messages.[1]

getopts was first introduced in 1986 in the Bourne shell shipped with Unix SVR3. It uses the shell's own variables to track the position of current and argument positions, OPTIND and OPTARG, and returns the option name in a shell variable.[2] Earlier versions of the Bourne shell did not have getopts.

In 1995, getopts was included in the Single UNIX Specification version 1 / X/Open Portability Guidelines Issue 4.[3] As a result, getopts is now available in shells including the Bourne shell, KornShell, Almquist shell, Bash and Zsh.[4]

The getopts command has also been ported to the IBM i operating system.[5]

The modern usage of <syntaxhighlight lang="text" class="" style="" inline="1">getopt</syntaxhighlight> was partially revived mainly due to an enhanced implementation in util-linux. This version, based on the BSD <syntaxhighlight lang="text" class="" style="" inline="1">getopt</syntaxhighlight>, not only fixed the two complaints around the old <syntaxhighlight lang="text" class="" style="" inline="1">getopt</syntaxhighlight>, but also introduced the capability for parsing GNU-style long options and optional arguments for options, features that <syntaxhighlight lang="text" class="" style="" inline="1">getopts</syntaxhighlight> lacks.[6] The various BSD distributions, however, stuck to the old implementation.[1]

Usage

The usage synopsis of getopt and getopts is similar to its C sibling:

getopt optstring [parameters]
getopts optstring varname [parameters]
  • The optstring part has the same format as the C sibling.
  • The parameters part simply accepts whatever one wants getopt to parse. A common value is all the parameters, "$@" in POSIX shell.
    • This value exists in getopts but is rarely used, since it can just access the shell's parameters. It is useful with resetting the parser, however.
  • The varname part of getopts names a shell variable to store the option parsed into.

The way one uses the commands however varies a lot:

  • getopt simply returns a flat string containing whitespace-separated tokens representing the "normalized" argument. One then uses a while-loop to parse it natively.[1]
  • getopts is meant to be repeatedly called like the C getopt. When it hits the end of arguments, it returns 1 (shell false).[3]

Enhancements

In various getopts

In spring 2004 (Solaris 10 beta development), the libc implementation for getopt() was enhanced to support long options. As a result, this new feature was also available in the built-in command getopts of the Bourne Shell. This is triggered by parenthesized suffixes in the optstring specifying long aliases.[7]

KornShell and Zsh both have an extension for long arguments. The former is defined as in Solaris,[8] while the latter is implemented via a separate <syntaxhighlight lang="text" class="" style="" inline="1">zparseopts</syntaxhighlight> command.[9]

KornShell additionally implements optstring extensions for options beginning with <syntaxhighlight lang="text" class="" style="" inline="1">+</syntaxhighlight> instead of <syntaxhighlight lang="text" class="" style="" inline="1">-</syntaxhighlight>.[8]

In Linux getopt

An alternative to getopts is the Linux enhanced version of getopt, the external command line program.

The Linux enhanced version of getopt has the extra safety of getopts plus more advanced features. It supports long option names (e.g. --help) and the options do not have to appear before all the operands (e.g. command operand1 operand2 -a operand3 -b is permitted by the Linux enhanced version of getopt but does not work with getopts). It also supports escaping metacharacters for shells (like tcsh and POSIX sh) and optional arguments.[6]

Comparison

Program
Feature
POSIX getopts Solaris/ksh getopts Unix/BSD getopt Linux getopt
Splits options for easy parsing Yes Yes Yes Yes
Allows suppressing error messages Yes Yes No Yes
Safe with whitespace and metacharacters Yes Yes No Yes
Allows operands to be mixed with options No Yes No Yes
Supports long options Emulation Yes No Yes
Optional arguments Error handling Error handling No Yes

Examples

Suppose we are building a Wikipedia downloader in bash that takes three options and zero extra arguments:

wpdown -a article name -l [language] -v

When possible, we allow the following long arguments:

-a   --article
-l   --language, --lang
-v   --verbose

For clarity, no help text is included, and we assume there is a program that downloads any webpage. In addition, all programs are of the form: <syntaxhighlight lang="bash">

  1. !/bin/bash

verbose=0 article= lang=en

  1. [EXAMPLE HERE]

if ((verbose > 2)); then

 printf '%s\n' 'Non-option arguments:'
 printf '%q ' "${remaining[@]]}"

fi

if ((verbose > 1)); then

 printf 'Downloading %s:%s\n' "$lang" "$article"

fi

if ! $article ; then

 printf '%s\n' "No articles!" >&2
 exit 1

fi

save_webpage "https://${lang}.wikipedia.org/wiki/${article}" </syntaxhighlight>

Using old getopt

The old getopt does not support optional arguments: <syntaxhighlight lang="sh">

  1. parse everything; if it fails we bail

args=`getopt 'a:l:v' $*` || exit

  1. now we have the sanitized args... replace the original with it

set -- $args

while true; do

   case $1 in
     (-v)   ((verbose++));  shift;;
     (-a)   article=$2; shift 2;;
     (-l)   lang=$2; shift 2;;
     (--)   shift; break;;
     (*)    exit 1;;           # error
   esac

done

remaining=("$@") </syntaxhighlight> This script will also break with any article title with a space or a shell metacharacter (like ? or *) in it.

Using getopts

Getopts give the script the look and feel of the C interface, although in POSIX optional arguments are still absent: <syntaxhighlight lang="sh">

  1. !/bin/sh

while getopts ':a:l:v' opt; do

   case $opt in
     (v)   ((verbose++));;
     (a)   article=$OPTARG;;
     (l)   lang=$OPTARG;;
     (:)   # "optional arguments" (missing option-argument handling)
           case $OPTARG in
             (a) exit 1;; # error, according to our syntax
             (l) :;;      # acceptable but does nothing
           esac;;
   esac

done

shift "$((OPTIND - 1))"

  1. remaining is "$@"

</syntaxhighlight> Since we are no longer operating on shell options directly, we no longer need to shift them within the loop. However, a slicing operation is required to remove the parsed options and leave the remaining arguments.

It is fairly simple to emulate long option support of flags by treating <syntaxhighlight lang="text" class="" style="" inline="1">--fast</syntaxhighlight> as an argument <syntaxhighlight lang="text" class="" style="" inline="1">fast</syntaxhighlight> to an option <syntaxhighlight lang="text" class="" style="" inline="1">-</syntaxhighlight>. That is, <syntaxhighlight lang="text" class="" style="" inline="1">-:</syntaxhighlight> is added to the optstring, and <syntaxhighlight lang="text" class="" style="" inline="1">-</syntaxhighlight> is added as a case for <syntaxhighlight lang="text" class="" style="" inline="1">opt</syntaxhighlight>, within which <syntaxhighlight lang="text" class="" style="" inline="1">OPTARG</syntaxhighlight> is evaluated for a match to <syntaxhighlight lang="text" class="" style="" inline="1">fast</syntaxhighlight>. Supporting long options with an argument is more tedious, but is possible when the options and arguments are delineated by =.[10]

Using Linux getopt

Linux getopt escapes its output and an "eval" command is needed to have the shell interpret it. The rest is unchanged: <syntaxhighlight lang="sh">

  1. !/bin/bash
  1. We use "${@}" instead of "${*}" to preserve argument-boundary information

args=$(getopt --options 'a:l::v' --longoptions 'article:,lang::,language::,verbose' -- "${@}") || exit eval "set -- ${args}"

while true; do

   case "${1}" in
       (-v | --verbose)
           ((verbose++))
           shift
       ;;
       (-a | --article)
           article=${2}
           shift 2
       ;;
       (-l | --lang | --language)
           # handle optional: getopt normalizes it into an empty string
           if [[ -n ${2} ]] ; then
               lang=${2}
           fi
           shift 2
       ;;
       (--)
           shift
           break
       ;;
       (*)
           exit 1    # error
       ;;
   esac

done

remaining_args=("${@}") </syntaxhighlight>

See also

References

  1. ^ 1.0 1.1 1.2 getopt(1) – FreeBSD General Commands Manual
  2. ^ Mascheck, Sven. "The Traditional Bourne Shell Family". Retrieved 2010-12-01.
  3. ^ 3.0 3.1 "getopts". The Open Group (POSIX 2018).
  4. ^ "Bash Reference Manual".
  5. ^ IBM. "IBM System i Version 7.2 Programming Qshell" (PDF). IBM. Retrieved 2020-09-05.
  6. ^ 6.0 6.1 getopt(1) – Linux General Commands Manual
  7. ^ "getopt(3)". Oracle Solaris 11.2 Information Library.
  8. ^ 8.0 8.1 "ksh getopts -- parse options from shell script command line". www.mkssoftware.com.
  9. ^ zshmodules(1) – Linux General Commands Manual
  10. ^ "A simple CLI parser in Bash".

External links