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?
There are three implementations that may be considered:
getopts. This does not support long option names with the double-dash prefix. It only supports single-character options.
BSD UNIX implementation of standalone
getopt command (which is what MacOS uses). This does not support long options either.
Some other answers show a solution for using the bash builtin
getopts to mimic long options. That solution actually makes a short option whose character is "-". So you get "--" as the flag. Then anything following that becomes OPTARG, and you test the OPTARG with a nested
This is clever, but it comes with caveats:
getoptscan't enforce the opt spec. It can't return errors if the user supplies an invalid option. You have to do your own error-checking as you parse OPTARG.
So while it is possible to write more code to work around the lack of support for long options, this is a lot more work and partially defeats the purpose of using a getopt parser to simplify your code.
getopts are different beasts, and people seem to have a bit of misunderstanding of what they do.
getopts is a built-in command to
bash to process command-line options in a loop and assign each found option and value in turn to built-in variables, so you can further process them.
getopt, however, is an external utility program, and it doesn't actually process your options for you the way that e.g. bash
getopts, the Perl
Getopt module or the Python
argparse modules do. All that
getopt does is canonicalize the options that are passed in — i.e. convert them to a more standard form, so that it's easier for a shell script to process them. For example, an application of
getopt might convert the following:
myscript -ab infile.txt -ooutfile.txt
myscript -a -b -o outfile.txt infile.txt
You have to do the actual processing yourself. You don't have to use
getopt at all if you make various restrictions on the way you can specify options:
-oabove), the value has to go as a separate argument (after a space).
getopt instead of
getopts? The basic reason is that only GNU
getopt gives you support for long-named command-line options.1 (GNU
getopt is the default on Linux. Mac OS X and FreeBSD come with a basic and not-very-useful
getopt, but the GNU version can be installed; see below.)
For example, here's an example of using GNU
getopt, from a script of mine called
# NOTE: This requires GNU getopt. On Mac OS X and FreeBSD, you have to install this # separately; see below. TEMP=`getopt -o vdm: --long verbose,debug,memory:,debugfile:,minheap:,maxheap: \ -n 'javawrap' -- "$@"` if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi # Note the quotes around `$TEMP': they are essential! eval set -- "$TEMP" VERBOSE=false DEBUG=false MEMORY= DEBUGFILE= JAVA_MISC_OPT= while true; do case "$1" in -v | --verbose ) VERBOSE=true; shift ;; -d | --debug ) DEBUG=true; shift ;; -m | --memory ) MEMORY="$2"; shift 2 ;; --debugfile ) DEBUGFILE="$2"; shift 2 ;; --minheap ) JAVA_MISC_OPT="$JAVA_MISC_OPT -XX:MinHeapFreeRatio=$2"; shift 2 ;; --maxheap ) JAVA_MISC_OPT="$JAVA_MISC_OPT -XX:MaxHeapFreeRatio=$2"; shift 2 ;; -- ) shift; break ;; * ) break ;; esac done
This lets you specify options like
--verbose -dm4096 --minh=20 --maxhe 40 --debugfi="/Users/John Johnson/debug.txt" or similar. The effect of the call to
getopt is to canonicalize the options to
--verbose -d -m 4096 --minheap 20 --maxheap 40 --debugfile "/Users/John Johnson/debug.txt" so that you can more easily process them. The quoting around
"$2" is important as it ensures that arguments with spaces in them get handled properly.
If you delete the first 9 lines (everything up through the
eval set line), the code will still work! However, your code will be much pickier in what sorts of options it accepts: In particular, you'll have to specify all options in the "canonical" form described above. With the use of
getopt, however, you can group single-letter options, use shorter non-ambiguous forms of long-options, use either the
--file foo.txt or
--file=foo.txt style, use either the
-m 4096 or
-m4096 style, mix options and non-options in any order, etc.
getopt also outputs an error message if unrecognized or ambiguous options are found.
NOTE: There are actually two totally different versions of
getopt and GNU
getopt, with different features and different calling conventions.2 Basic
getopt is quite broken: Not only does it not handle long options, it also can't even handle embedded spaces inside of arguments or empty arguments, whereas
getopts does do this right. The above code will not work in basic
getopt is installed by default on Linux, but on Mac OS X and FreeBSD it needs to be installed separately. On Mac OS X, install MacPorts (http://www.macports.org) and then do
sudo port install getopt to install GNU
getopt (usually into
/opt/local/bin), and make sure that
/opt/local/bin is in your shell path ahead of
/usr/bin. On FreeBSD, install
A quick guide to modifying the example code for your own program: Of the first few lines, all is "boilerplate" that should stay the same, except the line that calls
getopt. You should change the program name after
-n, specify short options after
-o, and long options after
--long. Put a colon after options that take a value.
Finally, if you see code that has just
set instead of
eval set, it was written for BSD
getopt. You should change it to use the
eval set style, which works fine with both versions of
getopt, while the plain
set doesn't work right with GNU
ksh93 supports long-named options, but this shell isn't used as often as
zparseopts to get this functionality.
getopt" is a misnomer; this version was actually written for Linux rather than the GNU project. However, it follows all the GNU conventions, and the term "GNU
getopt" is commonly used (e.g. on FreeBSD).