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

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

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 --" 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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.