I use getopts to parse arguments in bash scripts as

while getopts ":hd:" opt; do
  case $opt in
    d ) echo "directory = $OPTARG"; mydir="$OPTARG"; shift $((OPTIND-1)); OPTIND=1 ;;
    h ) helptext
      graceful_exit ;;
    * ) usage
      exit 1


exeparams will hold any unparsed options/arguments. Since I want to use exeparams to hold options for a command to be executed within the script (which can overlap with the scripts own options), I want to use -- to end the options passed to the script. If I pass e.g.

myscript -d myscriptparam -- -d internalparam

exeparams will hold

-- -d internalparam

I now want to remove the leading -- to pass these arguments to the internal command. Is there an elegant way to do this or can I obtain a string which holds just the remainder without -- from getopts?

  • Putting shift; OPTIND=1 inside the getopts loop is probably not the best way to do it. It only works in your case because you only have 2 options and in all the other ones you just exit the script. Otherwise you'd need shift; OPTIND=1 in every option, which means duplicate code (bad practice). Just do a shift $((OPTIND - 1)) immediately after the end of the loop - this is the most conventional way and probably the most efficient as well. – jw013 Nov 14 '12 at 22:40

How about:

# ... getopts processing ...

[[ $1 = "--" ]] && shift

Note, you should use an array to hold the parameters. That will properly handle any arguments containing whitespace. Dereference the array with "${exeparams[@]}"

  • 1
    This assumes that there are no arguments between the end of options and --, i.e. script foo -- bar would pass foo -- bar to the external program. My answer doesn't make that assumption as it wasn't explicitly stated in the question. – jw013 Nov 14 '12 at 23:05
  • Except where the OP says "I now want to remove the leading --" – glenn jackman Nov 14 '12 at 23:21
  • That was for the example -- -d internalparams. In any case my answer is general enough to handle either case. – jw013 Nov 14 '12 at 23:55

Use the built-in shift. First, do the normal getopts for your script. Once that loop completes,

shift "$((OPTIND - 1))"

will shift out all the already processed options.

From there, you'll have to finish processing the non-option arguments, if any, to the first part of the script (before the --). Once you encounter the --, shift it out until only the latter portion remains (the -d internalparam part that comes after --). One way to do this (using bash syntax):

while [[ $# -gt 0 ]]; do
    # process next argument
    case $1 in
    foo) # process foo
    --) shift; break;; # found '--', discard it and exit loop
    *) # handle unrecognized argument
    # not '--', so discard the argument and continue

Finally, only the second set of options / arguments remain, which you can pass on. Do NOT use $* to pass the remaining parameters to another command. Use "$@" instead, which preserves the original word splitting.

external_command "$@"
  • Yes, thanks! But how exactly do I find --? – highsciguy Nov 14 '12 at 22:20

Your Answer

