Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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?

share|improve this question
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
up vote 4 down vote accepted

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[@]}"

share|improve this answer
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 "$@"
share|improve this answer
Yes, thanks! But how exactly do I find --? – highsciguy Nov 14 '12 at 22:20

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

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