I have this bash code combined with getopts and if I understood getopts correctly OPTIND contains the index of the next command line option and all the command line options provided to shell script are presented in the variables $1, $2, $3 etc.. Correct me if I am wrong but basically the same concept as local variables in the functions.

So according to this why options [-a somevalue ] or [-b somevalue ] won't give me any results. What am I doing wrong?


while getopts :a:b FLAG; do
 case $FLAG in

shift $((OPTIND-1))

    while [ $# -ne 0 ]; do

if [[ -z $OPT_A ]]; then
    if [ `echo $1 | grep -o '\.' | wc -l` -ne 3 ]; then
        echo "Parameter '$1' does not look like an IP Address (does not contain 3 dots).";
            exit 1;
        elif [ `echo $1 | tr '.' ' ' | wc -w` -ne 4 ]; then
            echo "Parameter '$1' does not look like an IP Address (does not contain 4 octets).";
        exit 1;
        for OCTET in `echo $1 | tr '.' ' '`; do
                if ! [[ $OCTET =~ ^[0-9]+$ ]]; then
                        echo "Parameter '$1' does not look like in IP Address (octet '$OCTET' is not numeric).";
                        exit 1;
                elif [[ $OCTET -lt 0 || $OCTET -gt 255 ]]; then
                        echo "Parameter '$1' does not look like in IP Address (octet '$OCTET' in not in range 0-255).";
                        exit 1;

if [[ -z $OPT_B ]]; then
        if [[ "$2" =~ ^[0-9]+$ ]] && [ "$2" -ge 1 -a "$2" -le 10000 ]; then 
            echo "chosen variable: $2";
            exit 1;
            else echo "variable $2 is not in range '1 - 10000'";
            exit 1;

    exit 0
  • You put the : after the option letter to indicate that it takes an argument after it. So it should be a:b:, not :a:b.
    – Barmar
    Feb 27, 2015 at 16:29

3 Answers 3


It's because all of your logic depends on one of $OPT_[AB] being null. But even if you don't pass a -[ab] $OPTARG parameter, you're still setting them at the top of the script with OPT_[AB]=[AB]. So your logic chains never get past the root...

if [[ -z $OPT_A ]]; then...


Well... not all of your logic depends on that. You're also doing:

shift $((OPTIND-1))

    while [ $# -ne 0 ]...

So if you passed script -a arg then getopts would set $OPT_A to arg and $OPTIND would come to 3. So you would shift 2 (all of your positionals) then immediately fail the test in while. So your case would set $OPT_A and the next thing that happens is exit 0. So I guess you'd never even get to check for an empty $OPT_A anyway.

And even that would be ok since most of your logic is designed to test for failure - but your script only exits. You probably do the $OPT_A var, but you don't do anything with it. You can't use that value after the script exits - not without some preset IPC, but there's none of that here. The script is called in a subshell and the values it sets are lost when it returns to the parent shell.

What's more the optstring :a:b doesn't allow for an $OPTARG to -b. A leading colon in the optstring signifies quiet operation - it doesn't write to stderr if there's an issue with the options or their arguments. But a colon trailing an option char is what signifies that the option should expect an argument. Like:

while getopts :a:b: FLAG

...that would indicate two options that expect arguments. It can be tricky though because if you indicate that it is supposed to take an argument then if getopts finds it without one it flags it as an error:

sh -c 'getopts :a:b: opt -a; echo "$opt $OPTARG"'

...which prints...

: a

In that case the option winds up in $OPTARG and the : winds up in $opt. It is clearer if we're less :quiet about it:

sh -c 'getopts a:b: opt -a; echo "$opt $OPTARG"'
No arg for -a option

So, you need to check for : colon and for ? - which is another type of error and which is conventionally rerouted to print some short --help sort of thing.

Personally, I would make sure $OPT_[AB] were both empty to start with, do some logic on setting them correctly, and, when through with the test loop, make my last test one for a null value. If they haven't any value at all then it must be for some reason I haven't handled, and it is an error regardless. Here's a start at how I would go about working that test loop...

    set '' "$@"
    : "${1:?Parameter '$OPTARG' does not look like an IP Address $2}"

    oIFS=$IFS; unset "${IFS+o}oIFS"; IFS=.
    for oct in $1; do  [ $oct -lt 256 ] || 
        param_err "('$oct' too large)"
    done; unset IFS oct
    : "${oIFS+${IFS=$oIFS}}"

    case ${1##*.*.*.*.*} in (a) OPT_A=A;;  #some default - no arg provided                                            
          (.*|*..*|*.) param_err '(empty octet)';;
          (*[!.0-9]*)  param_err '(octet is not positive integer)';;
          (*.*.*.*)    test_oct  "$1"; OPT_A=$1;;
          (*?*)        param_err '(too few octets)';;
          (*)          param_err ${1:+"(too many octets)"} '(null param)';;

unset OPT_A OPT_B
while getopts :a:b:c:d: opt
do    case ${opt#:}$OPTARG in
      a*)   a_param "$OPTARG";;
      b*)   b_param "$OPTARG";; #tests as needed similar to a_param()
      ?*)   help;;              #param_err should call this too, but it just exits
  • Comments are not for extended discussion; this conversation has been moved to chat.
    – terdon
    Mar 3, 2015 at 0:26

You don't have a shebang line at the top, so the shell you are using (sh, dash) is likely not to support [[. You should start the script with:


or any other location bash is in, in the unlikely case that is not the path to bash on your system (type bash).

  • i add as you said, but there is still no improvement..
    – ticket
    Feb 27, 2015 at 15:58

Try changing this: while getopts :a:b FLAG; do

To This: while getopts :a:b: FLAG; do

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .