I've just found out that getopt
is not cross-platform (in particular for FreeBSD and Linux). What is the best workaround for this issue?

- 5,680
- 3
- 23
- 43
-
If you started out using GNU getopt with long options etc, and are looking to make your existing code portable with as few changes as possible, then you may find http://stackoverflow.com/a/37087374/324105 useful. – phils Oct 28 '16 at 23:18
4 Answers
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.

- 16,076
- 5
- 30
- 16
-
Yes, `getopts` (with an s) is another option. Portability purists would still prefer getopt since getopts was not available in ancient Bourne shells before 1986, but that's a poor reason since most/all modern shells support get opts. A better reason is to make it easy to take advantage of GNU enhanced getup if it is available. The GNU enhanced getopt allows operands to be mixed with options and supports long option names (both features that getopts does not support - see [comparison table][1]). – Hoylen Jun 17 '14 at 03:19
-
Why does it print an empty `--` when I run `eval set -- $ARGS`? It is very irritating. – Hindol Jul 01 '15 at 07:00
-
After I dig around, this turns out to be the best solution currently i can have with a little modification! I use `getopts` if `getopt` is not GNU-enhanced, so that whitespaces may be handled more properly and be compatible with the enhanced one. – Wenhao Ji Jan 29 '19 at 07:14
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.

- 1
- 1

- 346,391
- 90
- 374
- 439
-
-
@Dennis Williamson Please update your answer: According to the (updated) FAQ entry: **Unless it's the version from util-linux, and you use its advanced mode, never use getopt(1). ...** – Florian Oct 16 '15 at 11:10
-
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

- 1
- 1

- 2,537
- 1
- 13
- 5