I wish to have long and short forms of command line options invoked using my shell script.
I know that getopts
can be used, but like in Perl, I have not been able to do the same with shell.
Any ideas on how this can be done, so that I can use options like:
./shell.sh --copyfile abc.pl /tmp/
./shell.sh -c abc.pl /tmp/
In the above, both the commands mean the same thing to my shell, but using getopts
, I have not been able to implement these?
Builtin
getopts
only parse short options (except in ksh93), but you can still add few lines of scripting to make getopts handles long options.Here is a part of code found in http://www.uxora.com/unix/shell-script/22-handle-long-options-with-getopts
Here is a test:
Otherwise in recent Korn Shell ksh93,
getopts
can naturally parse long options and even display a man page alike. (See http://www.uxora.com/unix/shell-script/20-getopts-with-man-page-and-long-options)The Bash builtin getopts function can be used to parse long options by putting a dash character followed by a colon into the optspec:
After copying to executable file name=
getopts_test.sh
in the current working directory, one can produce output likeObviously getopts neither performs
OPTERR
checking nor option-argument parsing for the long options. The script fragment above shows how this may be done manually. The basic principle also works in the Debian Almquist shell ("dash"). Note the special case:Note that, as GreyCat from over at http://mywiki.wooledge.org/BashFAQ points out, this trick exploits a non-standard behaviour of the shell which permits the option-argument (i.e. the filename in "-f filename") to be concatenated to the option (as in "-ffilename"). The POSIX standard says there must be a space between them, which in the case of "-- longoption" would terminate the option-parsing and turn all longoptions into non-option arguments.
.
Here's an example that actually uses getopt with long options:
getopts "could be used" for parsing long options as long as you don't expect them to have arguments...
Here's how to:
If you try to use OPTIND for getting a parameter for the long option, getopts will treat it as the first no optional positional parameter and will stop parsing any other parameters. In such a case you'll be better off handling it manually with a simple case statement.
This will "always" work:
Albeit is not as flexible as getopts and you have to do much of the error checking code yourself within the case instances...
But it is an option.
Long options can be parsed by the standard
getopts
builtin as “arguments” to the-
“option”This is portable and native POSIX shell – no external programs or bashisms are needed.
This guide implements long options as arguments to the
-
option, so--alpha
is seen bygetopts
as-
with argumentalpha
and--bravo=foo
is seen as-
with argumentbravo=foo
. The true argument can be harvested with a simple replacement:${OPTARG#*=}
.In this example,
-b
(and its long form,--bravo
) has a mandatory option (note the manual reconstruction of enforcing that for the long form). Non-boolean options to long arguments come after equals signs, e.g.--bravo=foo
(space delimiters for long options would be hard to implement).Because this uses
getopts
, this solution supports usage likecmd -ac --bravo=foo -d FILE
(which has combined options-a
and -c
and interleaves long options with standard options) while most other answers here either struggle or fail to do that.When the argument is a dash (
-
), it has two more components: the flag name and (optionally) its argument. I delimit these the standard way any command would, with the first equals sign (=
).$LONG_OPTARG
is therefore merely the content of$OPTARG
without the flag name or equals sign.The inner
case
implements long options manually, so it needs some housekeeping:bravo=?
matches--bravo=foo
but not--bravo=
(note:case
stops after the first match)bravo*
follows, noting the missing required argument in--bravo
and--bravo=
alpha* | charlie*
catches arguments given to the options that don't support them''
is present to support non-options that happen to start with dashes*
catches all other long options and recreates the error thrown by getopts for an invalid optionYou don't necessarily need all of those housekeeping items. For example, perhaps you want
--bravo
to have an optional argument (which-b
can't support due to a limitation ingetopts
). Merely remove the=?
and the related failure case and then call${ARG_B:=$DEFAULT_ARG_B}
the first time you use$ARG_B
.