Cross-platform getopt for a shell script

為{幸葍}努か 提交于 2019-11-30 05:16:59

Use getopts (with an "s").

According to Bash FAQ 35:

Unless it's the version from util-linux, and you use its advanced mode, never use getopt(1). getopt cannot handle empty arguments strings, or arguments with embedded whitespace. Please forget that it ever existed.

The POSIX shell (and others) offer getopts which is safe to use instead.

There are essentially two versions of the getopt command: the original version and the GNU enhanced version. The GNU enhanced version is backward compatible with the original version, so if you only use the features of the original version it will work with both.

Detect which version of getopt is available

You can detect which version is available and use the enhanced features if the GNU enhanced version is available, and limit yourself to the original features if the GNU enhanced version is not available. The enhanced version has a -T option for testing which version is available.

getopt -T > /dev/null
if [ $? -eq 4 ]; then
    # GNU enhanced getopt is available
    set -- `getopt --long help,output:,version --options ho:v -- "$@"`
else
    # Original getopt is available
    set -- `getopt ho:v "$@"`
fi

Consider using built-in shell command getopts (with an "s") instead, because it is more portable. However, getopts does not support long options (e.g. --help).

If you like long options, use getopt and use the above test to see if the GNU enhanced version of getopt is available or not. If the enhanced version is not available, the script can gracefully degrade to either using the original version of getopt (with no support for long option names and no whitespace support) or using getopts (with no support for long option names).

Using GNU enhanced getopt properly

Getting the GNU enhanced version to process arguments with whitespace properly is tricky. Here's how it is done:

ARGS=`getopt --long help,output:,verbose --options ho:v -- "$@"`
if [ $? -ne 0 ]; then
  echo "Usage error (use -h for help)" >&2
  exit 2
fi
eval set -- $ARGS

# Parameters are now sorted: options appear first, followed by --, then arguments
# e.g. entering: "foo bar" -o abc baz -v
#      produces: -o 'abc' -v -- 'foo bar' 'baz'

The secret is to use "$@" where the double quotes are very important (in line 1), and to eval the set command (in line 6).

So errors raised by getopt can be detected and handled, the call to getopt is done separately from the eval with the two linked by the ARGS variable.

Complete working example

PROG=`basename $0`

getopt -T > /dev/null
if [ $? -eq 4 ]; then
  # GNU enhanced getopt is available
  ARGS=`getopt --name "$PROG" --long help,output:,verbose --options ho:v -- "$@"`
else
  # Original getopt is available (no long option names, no whitespace, no sorting)
  ARGS=`getopt ho:v "$@"`
fi
if [ $? -ne 0 ]; then
  echo "$PROG: usage error (use -h for help)" >&2
  exit 2
fi
eval set -- $ARGS

while [ $# -gt 0 ]; do
    case "$1" in
        -h | --help)     HELP=yes;;
        -o | --output)   OUTFILE="$2"; shift;;
        -v | --verbose)  VERBOSE=yes;;
        --)              shift; break;; # end of options
    esac
    shift
done

if [ $# -gt 0 ]; then
  # Remaining parameters can be processed
  for ARG in "$@"; do
    echo "$PROG: argument: $ARG"
  done
fi

echo "$PROG: verbose: $VERBOSE"
echo "$PROG: output: $OUTFILE"
echo "$PROG: help: $HELP"

This example can be downloaded from https://gist.github.com/hoylen/6607180

The comparison table on Wikipedia's entry on getopts compares the different features.

Basic syntax for getopt is cross-platform.

getopt vi: -v -i 100 file
Arvid Requate

The Bash builtin getopts function can be used to parse short and long options portably, see:

Using getopts in bash shell script to get long and short command line options

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!