I've been exploring this issue for longer than it should take me and finding getopts a very confusing tool.

All I want to do is the following. Have a script that I can pass arguments like this $1 $2 $3 and one of them being an optional -e email

So this is what I did, which of course doesn't work at all:


    if [[ $# -lt 2 ]] || [[ $# -gt 3 ]]
            echo "usage is `basename $0` argument1 argument2 {-e email}"
    exit 1

    while getopts e: flag; do
      case $flag in

    [[ -v $EMAIL ]] && echo "I am sending you $1 and $2!!" | mutt -s "present" $EMAIL && exit 0

    echo "I am keeping $1 and $2 to myself"

Of course I could just ignore this getopts business and do without it, I am just trying to learn how to use it properly


You have multiple problems:

  • You are testing for the number of arguments before options have been processed. Before options are processed you don't know how many slots in argv are occupied by options so you can't decide whether $# needs to be between 2 and 3 (as you propose), or something else. Even if you predict the presence of a -e option, you don't know whether it will come as -eemail (consumes 1 argv slot) or -e email (consumes 2 slots). It sounds like you want to receive exactly 2 non-option arguments, so what you need to do is check that $# is exactly equal to 2 after options have been consumed.
  • [[ -v $EMAIL ]] email does not make any sense. This tests whether the value of $EMAIL designates a valid shell variable, which is not what you want. You meant something like [ -n "$EMAIL" ]
  • getopts does not consume the options, it only parses them. After parsing them, you should skip past them with shift $(($OPTIND-1)).
  • You are invoking the script incorrectly. Options come before arguments, so the usage is $0 [-e email] argument1 argument2.
  • Thank you, your answer helped understand the problem a bit more, still not sure whether I am a fan of getopts ;-) on your -v point, I somewhat disagree with you. (unix.stackexchange.com/questions/212183/…) You are right you can use ` -n` or ! -z to check a variable exists but -v should also work in latter versions of bash. And it does work in my tests. – Ulukai Mar 28 '16 at 11:02
  • Sure, [[ -v EMAIL ]] ought to work (but not [[ -v $EMAIL ]] as you wrote!) however why use a non-portable non-standard bashism when a standard construct like [ -n "$EMAIL" ] will work just as well, be more portable, probably be understood by more readers, and is not longer to type? – Celada Mar 28 '16 at 11:06

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.