4

I would like to parse long options in a shell script. POSIX only provides getopts to parse single letter options. Does anyone know of a portable (POSIX) way to implement long option parsing in the shell? I've looked at what autoconf does when generating configure scripts, but the result is far from elegant. I can live with accepting only the full spellings of long options. Single letter options should still be allowed, possibly in groups.

I'm thinking of a shell function taking a space separated list of args of the form option[=flags], where the flags indicate that the option takes an arg or can be specified multiple times. Unlike its C counterpart there is no need to distinguish between strings, integers and floats.

Jens
  • 69,818
  • 15
  • 125
  • 179
  • POSIX doesn't recognize long options, so there won't be a portable POSIX way to do it. See [Using getopts in bash shell script to get long and short command line options](http://stackoverflow.com/questions/402377) amongst other related questions. – Jonathan Leffler Aug 07 '12 at 14:10
  • I meant a shell function written using POSIX sh. No bashisms or zshisms. That is surely possible, but before I reinvent the wheel... Thanks for the link. – Jens Aug 07 '12 at 14:16
  • http://stackoverflow.com/a/37087374/324105 is a POSIX sh function specifically intended for replacing uses of GNU getopt (which handles short/long options with mandatory/optional/no arguments). – phils Oct 28 '16 at 23:08

1 Answers1

2

Design notes towards a portable shell getopt_long command

I have a program getoptx which works with single-letter options (hence it is not the answer to your problem), but it handles arguments with spaces correctly, which the original getopt command (as opposed to the shell built-in getopts) does not. The specification in the source code says:

/*
** Usage: eval set -- $(getoptx abc: "$@")
**        eval set -- $(getoptx abc: -a -c 'a b c' -b abc 'd e f')
** The positional parameters are:
** $1 = "-a"
** $2 = "-c"
** $3 = "a b c"
** $4 = "-b"
** $5 = "--"
** $6 = "abc"
** $7 = "d e f"
**
** The advantage of this over the standard getopt program is that it handles
** spaces and other metacharacters (including single quotes) in the option
** values and other arguments.  The standard code does not!  The downside is
** the non-standard invocation syntax compared with:
**
**        set -- $(getopt abc: "$@")
*/

I recommend the eval set -- $(getopt_long "$optspec" "$@") notation for your getopt_long.

One major issue with getopt_long is the complexity of the argument specification — the $optspec in the example.

You may want to look at the notation used in the Solaris CLIP (Command Line Interface Paradigm) for the notation; it uses a single string (like the original getopt() function) to describe the options. (Google: 'solaris clip command line interface paradigm'; using just 'solaris clip' gets you to video clips.)

This material is a partial example derived from Sun's getopt_clip():

/*

Example 2: Check Options and Arguments.

The following example parses a set of command line options and prints
messages to standard output for each option and argument that it
encounters.

This example can be expanded to be CLIP-compliant by substituting the
long string for the optstring argument:

While not encouraged by the CLIP specification, multiple long-option
aliases can also be assigned as shown in the following example:

:a(ascii)b(binary):(in-file)(input)o:(outfile)(output)V(version)?(help)

*/

static const char *arg0 = 0;

static void print_help(void)
{
    printf("Usage: %s [-a][-b][-V][-?][-f file][-o file][path ...]\n", arg0);
    printf("Usage: %s [-ascii][-binary][-version][-in-file file][-out-file file][path ...]\n", arg0);
    exit(0);
}

static const char optstr[] =
    ":a(ascii)b(binary)f:(in-file)o:(out-file)V(version)?(help)";

int main(int argc, char **argv)
{
    int c;
    char *filename;

    arg0 = argv[0];
    while ((c = getopt_clip(argc, argv, optstr)) != -1)
    {
        ...
    }
    ...
}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278