Identify problem with while getopts

can anyone spot a problem with the below:

$ 
$ cat getopts.sh                                                                                                                               
#!/bin/sh

usage() { echo "myscript.sh local /tmp data.txt 600s -query" 1>&2; exit 1; }

while getopts ":::::::::::::i:e:" o; do
    case "${o}" in
        i)
            i=${OPTARG}
                if awk -v TOPTION="${i}" 'BEGIN{ if (TOPTION ~ /include:/) {exit 0} else {exit 1} }' ; then
                        echo "${i}"
                else
                        usage
                fi
            ;;
        e)
            e=${OPTARG}
                if awk -v TOPTION="${e}" 'BEGIN{ if (TOPTION ~ /exclude:/) {exit 0} else {exit 1} }' ; then
                        echo "${e}"
                else
                        usage
                fi
            ;;
        *)
            usage
            ;;
    esac
done
shift $((OPTIND-1))

if [ -z "${i}" ] || [ -z "${e}" ]; then
    usage
fi

echo "i = ${i}"
echo "e = ${e}"
$ 
$ 
$ 
$ ./getopts.sh local /tmp data.txt 50s -query -i include:
myscript.sh local /tmp data.txt 600s -query
$ 
$ 
$ ./getopts.sh -i include:                               
include:
myscript.sh local /tmp data.txt 600s -query
$ 
$ 

i only seem to get the expected output IF i specify just the "-i include", without anything else.

how can this be fixed? what am i doing wrong? i want to be able to specify the "-i" or "-e" or any additional arguments, wherever i want on the command line. how is this possible?
i need this to be portable as i'll be using it across many platforms.

It is not! According to the standards, options must be specified on the command line BEFORE operands.

Furthermore, I have no idea why you have so many colons in the first argument passed to getopts ??? Are you trying to say that you will allow up to six -: :_option_argument options??? You don't have a : choice in your case statement and you don't need to specify an option more than once in that operand even if more than one of those options are allowed.

I would try to write code that might work for you, but I have no idea what you are trying to do. The Usage statement printed by your code when things go wrong indicate that absolutely no options are allowed.

The getopts man page on your system or the man page for your shell on your system may supply an option (or an environment variable setting) that will allow you to violate POSIX rules and allow you to process command-line arguments with options after operands by reordering your command line. My system doesn't allow that without lots of extra abnormal argument processing and I won't help you do that because I believe that any new application written to put options after operands is fundamentally broken. (The only exceptions to this are utilities like compilers and linkers that need to apply certain options to certain groups of operands and different options to different groups of operands. That does not seem to be the case for what you are trying to do.)

And, what is the use of having "options" -i optional_string1include:optional_string2 and -e optional_string3exclude:optional_string4 if you script is going to fail if both of those options are not specified on the command line? If an option has to be specified, by definition, it is not an option! Did you just forget to supply default values for these options, or are they just additional required operands and you are trying to say that the order of those operands in relation to other required operands is unspecified??? (If this is what you're saying, I personally believe this is a horrendous human factors decision.)

i apologize for not being clear.

currently, there are 4 ways to run my script.

one is without any of the -i or -e options being passed to it
second is if just the -i is passed to it
third is if just the -e is pass to it
fourth is if both -i and -e are passed to the script

all im trying to do is account for those 4 scenarios using getopts.

If you are trying to have 4 scenarios, why does you script run the usage macro in three of the 4 scenarios (only moving on to the final two echo commands in your script if both options are present on the command line?

Please describe what you want your script to do in these 4 scenarios. Without knowing what these 4 scenarios are, I don't see how we can guess at how to correct your script.

And, what is the format of the -i and -e option-arguments. From your code I assume that the strings include: or exclude: , respectively, must occur somewhere in the option-arguments (not necessarily at the start of those option-arguments ), but why these strings are needed (rather than assumed from the option-letter) is not clear, what else is expected in those option-arguments is not clear, and whether or not some default value should be used for these option-arguments if the options are not specified on the command line is not clear.

Please also show us an actual synopsis for your utility showing the utility name, the options, and operands that are expected in the various valid manners in which your utility can be invoked. (For example, the string -query appears at the end of your usage statement. Is that an indication that there are 4 more options ( -q , -u , -r , and -y as well as a -e without an option-argument), or is it another mandatory string that is supposed to appear as the final operand to your utility?

You seem to have several operands that are required to be constant pathnames. Why does a user have to type in a constant pathname when your script could supply it automatically without the user having to type it in every time your script is invoked?

1 Like

Hi.

The *nix body of code suggests that options should come before arguments. So I am not surprised that you will not find support in standard utilities that goes beyond that.

However, there are other codes that do support mixed arguments and options. For example:
1) code getopt from util-linux at kernel.org, possibly in a repository (it was in Debian, from which I installed it): util-linux - Wikipedia, the free encyclopedia
A stand-alone version of source is available at:
Getopt | frodo.looijaard.name
Because ultimately it's a binary:

$ file /usr/bin/getopt
/usr/bin/getopt: ELF 64-bit LSB executable, x86-64, version 1 (SYSV),
 dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, 
BuildID[sha1]=31f6f91c2197e0da68251db8ee918c8c56c250d4, stripped

it should be able to be compiled anyplace there is a building environment.

2) A Google code, shFlags :

excerpt from Create new page · kward/shflags Wiki · GitHub
code at GitHub - kward/shflags: shFlags is a port of the Google gflags library for Unix shell.
documentation at: Documentation12x · kward/shflags Wiki · GitHub

That seems like a lot of extra work just to allow the mix of options and arguments -- probably too much for me in most instances -- but if you are sufficiently motivated, it can be done. In essence, you are rolling your own command-line processing with the support from those 2 projects (probably among many others about which I do not know).

Good luck ... cheers, drl

1 Like