views:

20187

answers:

6

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?

+15  A: 

The bash getopts builtin does not support long option names with the double-dash prefix. It only supports single-character options.

There is a shell tool getopt which is another program, not a bash builtin. The GNU implementation of getopt (e.g. on Linux) supports parsing long options.

But the BSD implementation of getopt (e.g. on Mac OS X) does not.

Bill Karwin
Were you reading over my shoulder? :D
Jonathan Leffler
Great minds think alike!
Bill Karwin
Use getopts folks, not getopt.http://bash-hackers.org/wiki/doku.php/howto/getopts_tutorial
hendry
@hendry: Can you explain why you say that? Even that article you link to has a prominent notice that getopts **doesn't** support long option names as the OP asks for.
Bill Karwin
So. What is the cross-platform, portable solution?
troelskn
GNU Getopt seems to be the only choice. On Mac, install GNU getopt from macports. On Windows, I'd install GNU getopt with Cygwin.
Bill Karwin
[Apparently](http://compute.cnr.berkeley.edu/cgi-bin/man-cgi?getopts+1), ksh getopts *can* handle long options.
Tgr
+4  A: 

The built-in getopts can't do this. There is an external getopt(1) program that can do this, but you only get it on Linux from the util-linux package. It comes with an example script getopt-parse.bash.

There is also a getopts_long written as a shell function.

Cirno de Bergerac
+8  A: 

The built-in getopts command is still, AFAIK, limited to single-character options only.

There is (or used to be) an external program getopt that would reorganize a set of options such that it was easier to parse. You could adapt that design to handle long options too. Example usage:

aflag=no
bflag=no
flist=""
set -- $(getopt abf: "$@")
while [ $# -gt 0 ]
do
    case "$1" in
    (-a) aflag=yes;;
    (-b) bflag=yes;;
    (-f) flist="$flist $2"; shift;;
    (--) shift; break;;
    (-*) echo "$0: error - unrecognized option $1" 1>&2; exit 1;;
    (*)  break;;
    esac
    shift
done

# Process remaining non-option arguments
...

You could use a similar scheme with a getoptlong command.

Jonathan Leffler
+1 for small example
Leonel
getopt, except for the GNU version (which has a different calling convention), is fundamentally broken. Do not use it.Please use **getopts instead http://bash-hackers.org/wiki/doku.php/howto/getopts_tutorial
hendry
+4  A: 

getopt and getopts become very frustrating to use, especially when one has many flags. Keeping the list of options in sync with the case statement used is one thing, but if you need to update a 'usage' function which outputs a listing of all the flags, you easily forget to document your flags.

Take a look at shFlags http://code.google.com/p/shflags/ which is a portable shell library (meaning sh, bash, dash, ksh, zsh on Linux, Solaris, etc.). I wrote to wrap getopt to make the whole process easier. It makes adding new flags as simple as adding one line to your script, and it provides an auto generated usage function.

Here is a simple 'Hello, world!' using shFlags.

#!/bin/sh

# source shflags from current directory
. ./shflags

# define a 'name' command-line string flag
DEFINE_string 'name' 'world' 'name to say hello to' 'n'

# parse the command-line
FLAGS "$@" || exit 1
eval set -- "${FLAGS_ARGV}"

# say hello
echo "Hello, ${FLAGS_name}!"

For OSes that have the enhanced getopt that supports long options (e.g. Linux), you can do:

$ ./hello_world.sh --name Kate
Hello, Kate!

For the rest, you must use the short option:

$ ./hello_world.sh -n Kate
Hello, Kate!

Adding a new flag is as simple as adding a new DEFINE_ call.

kate

Kate Ward
A: 

sudo port install getopt #external program on Mac OS X

Name
+1  A: 
#!/bin/bash
while getopts "abc:d:" flag
do
  case $flag in
    a) echo "[getopts:$OPTIND]==> -$flag";;
    b) echo "[getopts:$OPTIND]==> -$flag";;
    c) echo "[getopts:$OPTIND]==> -$flag $OPTARG";;
    d) echo "[getopts:$OPTIND]==> -$flag $OPTARG";;
  esac
done

shift $((OPTIND-1))
echo "[otheropts]==> $@"

exit

.

#!/bin/bash
until [ -z "$1" ]; do
  case $1 in
    "--dlong")
      shift
      if [ "${1:1:0}" != "-" ]
      then
        echo "==> dlong $1"
        shift
      fi;;
    *) echo "==> other $1"; shift;;
  esac
done
exit
3ED