1

I wrote a bash script that takes flexible number of parameters and now I would like to add an optional argument (-l) to each of them.

I am currently having difficulty getting the desired behavior.

I want all of the following to execute correctly:

./Script.sh arg1 arg2 arg3 -l opt 
./Script.sh arg1 arg2 arg3
./Script.sh arg1 arg2 arg3 arg4 -l opt
./Script.sh arg1 arg2 arg3 arg4 arg5

The problem is that $OPTIND cannot be set. The following loop works if the -l opt is placed before first argument.

while getopts ":l:" option
do
    case "$option" in
        t) 
            F_NAME=$OPTARG 
            ;;
    esac
done
shift $((OPTIND - 1))

However, place the optional -l as last parameter is a requirement. What's the easiest way to achieve this?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
ogs
  • 1,139
  • 8
  • 19
  • 42
  • Possible duplicate of [Optional option argument with getopts](http://stackoverflow.com/questions/11517139/optional-option-argument-with-getopts) – kenorb Dec 30 '15 at 12:35

2 Answers2

3

Here is a trick I have found to use arguments with optional parameters with getopts.

The way to manage the case where the optional parameter is inside the command is given by Jan Schampera in is reply on bash-hackers.org :

  if [[ $OPTARG = -* ]]; then
    ((OPTIND--))
    continue
  fi

(see : http://wiki.bash-hackers.org/howto/getopts_tutorial deep in the page) But it does not manage the case where the option is given at the end of the command.

In that case, that is considered wrong because no parameter is given, getopts set the opt variable to ':' (colon) and OPTARG to the fault option value. So we have to manage the ':' case, with a case $OPTARG.

Let us say we write a script having three options :

  • a : without parameter
  • b : with a required parameter
  • v : to set the verbosity with a value from 0 to 2. The default value is 0 and a preset value is used when the script is called with -v without parameter or with a bad value.

Here is the code :

#!/bin/bash

VERBOSITY=0 # default verbosity set to 0
PRESET_VERBOSITY=1 # preset verbosity when asked

while getopts :ab:v: opt; do
  case $opt in
    a)
      echo "manage option a"
    ;;
    b)
      echo "manage option b with value '$OPTARG'"
    ;;
    v)
      if [[ $OPTARG = -* ]]; then # Jan Schampera reply
        echo "set verbosity to PRESET (no value given, not last position)"
        VERBOSITY=$PRESET_VERBOSITY
        ((OPTIND--))
        continue
      fi

      if [[ "$OPTARG" =~ ^[0-2]$ ]]; then
        echo "set verbosity to $OPTARG (good value given)"
        VERBOSITY=$OPTARG
      else
        echo "set verbosity to PRESET (bad value given)"
        VERBOSITY=$PRESET_VERBOSITY
      fi
    ;;
    :)
      case $OPTARG in
        v)
          echo "set verbosity to PRESET (no value given, last option)"
          VERBOSITY=$PRESET_VERBOSITY
        ;;
      esac
    ;;
    \?)
      echo "WTF!"
    ;;
  esac
done

echo "**verbosity is set to $VERBOSITY**"
valiano
  • 16,433
  • 7
  • 64
  • 79
FrViPofm
  • 345
  • 2
  • 10
1

getopts conforms to the posix-standard command-line syntax where flag options come first. So it's not easy to use for non-standard cases.

However, you may have the Gnu implementation of getopt(1) (see man 1 getopt), which can handle permuted option flags as well as long options. However, it's not as easy an interface.

Or you can just interpret the argument yourself.

for ((i=1; i<=$#; ++i)); do
  if [[ ${!i} == "-l" ]]; then
    ((++i))
    OPT_L=${!i}
  else
    # handle the argument (in "${!i]")
  fi
done

(Note: the above does not throw an error if the -l appears right at the end of the argument list; it just sets the option value to an empty string. If that's not appropriate, which it probably isn't, then insert some error checking.)

rici
  • 234,347
  • 28
  • 237
  • 341
  • Basically, because flag options come first it is more suitable to pass the otp argument as first ? Your solution can probably satisfy the requirement but I am not sure that the "handle argument" part between else if could be standardise to satisfy each possible scenarios, no ? – ogs Mar 03 '15 at 23:30
  • @SnP: That's correct; that little bash snippet is not intended to be general purpose. I personally just use getopts and put the option parameters first, but `getopt` is a general-purpose solution (if you have the Gnu version), which is why I put it first in the answer. – rici Mar 04 '15 at 01:53