Reputation: 59
What is the meaning of the following template in getopts
?
while getopts ':x:y-:' val;
I know that it expects two options -x
or -y
but what is the meaning of the symbol -
at the end of the template ?
Upvotes: 4
Views: 131
Reputation: 5211
I was unable to find any documentation about this "-" in the option string. So, I tried an empirical approach to see how it influences the behavior of getopts
. I found that passing "--something" to the script (without spaces after "--") makes it accept "--" as an option and report "something" in OPTARG
:
#!/bin/bash
xopt=
yopt=
mopt=
while getopts ':x:y-:' val
do
case $val in
x) xopt=1
xval="$OPTARG";;
y) yopt=1;;
-) mopt=1
mval="$OPTARG";;
?) echo "Usage: $0: [-x value] [-y] [--long_opt_name] args" >&2
exit 2;;
esac
done
[ ! -z "$xopt" ] && echo "Option -x specified with parameter '$xval'"
[ ! -z "$yopt" ] && echo "Option -y specified"
[ ! -z "$mopt" ] && echo "Option -- specified with optname '$mval'"
shift $(($OPTIND - 1))
echo "Remaining arguments are: $*"
Examples of executions:
$ t.sh --v
Option -- specified with optname 'v'
Remaining arguments are:
$ t.sh --vv other1 other2
Option -- specified with optname 'vv'
Remaining arguments are: other1 other2
$ t.sh --help -x 123 -y others
Option -x specified with parameter '123'
Option -y specified
Option -- specified with optname 'help'
Remaining arguments are: others
$ t.sh --help -x 123 -- -y others
Option -x specified with parameter '123'
Option -- specified with optname 'help'
Remaining arguments are: -y others
$ t.sh -y -x val --x -- param1 -h -j -x -y
Option -x specified with parameter 'val'
Option -y specified
Option -- specified with optname 'x'
Remaining arguments are: param1 -h -j -x -y
Would it be a "hidden" feature to manage gnu-like long options but without parameters (i.e. only the "--long_opt_name") or am I promoting the side effect of a bug? Anyway, using such undocumented behavior is not advised as this may change after some future fixes or evolution of the command.
Nevertheless, if spaces are put after the double "-", the latter continues to play its usual documented role separating options from additional parameters:
$ t.sh --help -y -x val -- param1 -h -j -x -y
Option -x specified with parameter 'val'
Option -y specified
Option -- specified with optname 'help'
Remaining arguments are: param1 -h -j -x -y
$ t.sh -- -v
Remaining arguments are: -v
As getopts
is a builtin of bash
, I downloaded its source code (version 5.0) from here. The builtins are located in the eponym sub-directory. getopts
source code is: builtins/getopts.def. For each argument on the command line, it calls sh_getopt(argc, argv, optstr)
. This function is defined in builtins/getopt.c:
[...]
int
sh_getopt (argc, argv, optstring)
int argc;
char *const *argv;
const char *optstring;
{
[...]
/* Look at and handle the next option-character. */
c = *nextchar++; sh_charindex++;
temp = strchr (optstring, c);
sh_optopt = c;
/* Increment `sh_optind' when we start to process its last character. */
if (nextchar == 0 || *nextchar == '\0')
{
sh_optind++;
nextchar = (char *)NULL;
}
if (sh_badopt = (temp == NULL || c == ':'))
{
if (sh_opterr)
BADOPT (c);
return '?';
}
if (temp[1] == ':')
{
if (nextchar && *nextchar)
{
/* This is an option that requires an argument. */
sh_optarg = nextchar;
/* If we end this ARGV-element by taking the rest as an arg,
we must advance to the next element now. */
sh_optind++;
}
else if (sh_optind == argc)
{
if (sh_opterr)
NEEDARG (c);
sh_optopt = c;
sh_optarg = ""; /* Needed by getopts. */
c = (optstring[0] == ':') ? ':' : '?';
}
else
/* We already incremented `sh_optind' once;
increment it again when taking next ARGV-elt as argument. */
sh_optarg = argv[sh_optind++];
nextchar = (char *)NULL;
}
return c;
}
In the previous source lines, nextchar points on the option character (i.e. the one located right after '-') in argv[] and temp points on the option character in the optstring (which is '-'). We can see when temp[1] == ':' (i.e. the optstring specifies "-:"), sh_optarg is set with the incremented value of nextchar which is the first letter of the option name located behind "--".
In our example, where optstring is ":x:y-:" and we pass to the script "--name", the above code does:
optstring = ":x:y-:"
^
|
temp
argv[x] = "--name"
^^
/ \
c nextchar (+ 1)
temp[1] == ':' ==> sh_optarg=nextchar="name"
Hence, with the above algorithm in bash
, when ":-" is specified in the option string, any "--name" option on the command line reports "name" in OPTARG variable.
This is merely the output of the code path when the parameter is concatenated to the option name (e.g. -xfoo = option "x" and parameter "foo", --foo = option "-" and parameter "foo").
Upvotes: 3