12

Is it possible to ignore unknown optional arguments with GNU getopt?

I have a script, scriptA.sh, that has optional arguments --optA, --optB, --optC, --optD.

I would like to write a wrapper, wrapperA, with two optional arguments, --optX and --optY, that calls scriptA. However, I don't want to declare all optional parameters of scriptA inside the wrapper.

In particular, if inside wrapperA, I specify optional arguments with

getopt --longoptions optX:,optY:

the call

wrapperA --optX --optA --optB

returns an error

getopt: unknown option -- optA

Can GNU getopt be forced to ignore unknown arguments and place them after the '--' in its output?

hnk
  • 2,216
  • 1
  • 13
  • 18
user3830744
  • 131
  • 1
  • 5

5 Answers5

6

It is not possible to tell GNU getopt to ignore unknown options. If you really want that feature you will have to write your own option parser.

It is not as simple as to just ignore unknown options. How can you tell whether an unknown option takes an argument or not?

Example usage of original script:

originalscript --mode foo source

here foo is an argument to the option --mode. while source is a "non-option parameter" (sometimes called "positional parameter").

Example usage of wrapper script:

wrapperscript --with template --mode foo source

How can getopt in wrapperscript know that it should ignore --mode together with foo? If it just ignores --mode then originalscript will get foo as first positional parameter.

A possible workaround is to tell the users of your wrapper script to write all options intended for the original scrip after a double dash (--). By convention a double dash marks the end of options. GNU getopt recognizes double dash and stops parsing and returns the rest as positional parameters.

See also:

Lesmana
  • 25,663
  • 9
  • 82
  • 87
  • Thank you for your response. Double dash to terminate option list will work in my case. As for ignoring unknown options, I see your point - it is not as easy as it looks. My original idea was to push all unknown options behind -- in getopt output (--with template -- mode foo source). However, this would work only if subsequent parameter handling is handed off to the originalscript. As you said, wrapperscript has no way of knowing if parameters of originalscript take arguments. – user3830744 Jul 12 '14 at 00:24
  • Had a similar problem, the double dash works as a charm. – Andrei LED Nov 30 '15 at 18:39
1

I was working on a similar thing, and found this to work to stop getopt errors from bugging me with these errors. Basically just pipe the errors to oblivion.

while getopts "i:s:" opt > /dev/null 2>&1; do
    case $opt in
      i)
        END=$OPTARG
      ;;
    esac
done
./innerscript $*

$ ./blah.sh -s 20140503 -i 3 -a -b -c

michael salmon
  • 398
  • 4
  • 12
0

For getopt, at least for one from util-linux-2.37.2, STDERR can be ignored or --quiet flag can be used:

$ getopt --quiet --options=c: --longoptions=component -- --component value --foo
--component -- 'value'

Note that exit status will still be 1, as validation of arguments can’t be done by getopt, as it can’t know that what kind of errors you want to ignore.

Smar
  • 8,109
  • 3
  • 36
  • 48
-2

If you want to just ignore stderr, appending 2>dev/null to the starting line of while as below is OK. I think you can use this also for getopt.

while getopts "a:p:" opt 2>/dev/null
do
    case $opt in
      \?)
        echo "any error comment you want" 1>&2
        exit 1
        ;;
    esac
done
-2

Well, I could work around the problem. The idea involves fiddling with OPTIND when facing an unknown option, so that the parsing could be continued by getopts. Consequently we would need to keep where we were are at parsing so that OPTIND can be adjusted accordingly.

local parse_index=1 # initialize to cater for the first option coming out unknown
while getopts ':m:t:r:u:p:' opt; do
  case $opt in
    m) build_module="$OPTARG"
       parse_index=$OPTIND
       ;;
    t) build_tag="$OPTARG"
       parse_index=$OPTIND
       ;;
    r) container_registry="$OPTARG"
       parse_index=$OPTIND
       ;;
    u) registry_username="$OPTARG"
       parse_index=$OPTIND
       ;;
    p) registry_password="$OPTARG"
       parse_index=$OPTIND
       ;;
    :) echo "Option -$OPTARG requires an argument" >/dev/stderr
       ;;
   \?) echo "Unknown option: -$OPTARG"
       ((OPTIND = $parse_index + 2))    # fool `getopts` to continue parsing
       parse_index=$OPTIND      # adjust to cater for successive unknown options
       ;;
  esac
done

I have used an assumption that encountered unknown options would follow by an argument so the part ((OPTIND = $parse_index + 2)), adapt to your own case. The assumption proves brittle and non-inclusive, but works under those circumstance. Also note that still it discontinues in case of an error (missing argument).

Apparently, not a general and clean approach, nevertheless helped overcome the situation where no other alternative found.