Search code examples
bashfunctionsyntaxgetopts

Bash: Getopts doesn't run core loop ever


I was starting to understand getopts when it just stopped running all together. When I try getopts in small individual tests it works perfectly. I have been staring at my screen for a couple days now checking what's wrong, I even used a shellchecker which just said everything should work. I tried to echo inside the getopts while loop and it never printed, much less the echo's I had in the cases. Here is essentially what my code is,

helpmenu(){
   case $1 in
      general)
         echo "Usage, some operationtypes, -h or --help idenifier explained"
         ;;
      eachoperationtype)
         echo "Explain this operationtype"
         ;;

}
operationtype=$1
if [[ operationtype == "Operation1" ]]; then

   # Setting some variables to default values here
   var1=0 #Default values
   var2=4
   var3=5
   ...

   echo "this prints" # this prints

   while getopts ":hs:d:m:f:a" opt; do

           echo "does not print" # this does not print

           case ${opt} in
                   h )
                       helpmenu operationtype #Simple function that prints out script usage
                       ;;
                   s )
                       var=$OPTARG
                       ;;
                d )
                       var2=$OPTARG
                       ;;       
                m )
                       var3=$OPTARG
                       ;;
                f )
                       var4=$OPTARG
                       ;;
                a )
                       bool=true
                       ;; 
                \? )
                    echo "Syntax error: Unrecognised flags given"
                    helpmenu operationtype
                    ;;
                : )
                    echo "Syntax error: Invalid number of arguments"
                    helpmenu general # No arguments given - shows help menu with operationtypes so they can ./Script operationtype -h
                    ;;
        esac
   done 
   shift $((OPTIND -1))

   # Do some other stuff, works fine

#Other operations have similar structure to operationtype1
elif [[ operationtype == "operation2" ]]; then
   # Similar program, potentially different flags, function, meaning etc
fi

The main point of the script is to take in an initial first argument that is the type of operation that is required, and to run different getopts in different elifs after the initial if. Each one would have a specific amount of arguments that it accepted along with a list of optional flags. i.e. you could have one script that does ./Script greetings "hello" -t 3 which could for example echo "hello" 3 times and have another function in the same script./Script age -u User1 that gives the age of user1 in a list for example. Another issue that I am dealing with is the setting of the required variables, the required variables should just be normally gathered without flags. The issue with this is I want a help menu to pop up (echo out a quick usage) for each operationtype - i.e. ./Script operation1 -h returns operation1's help menu however I cannot allow this syntax as it will return an error when trying to set the required arguments. Also I need to make sure that they putting exactly the right amount of required args for each operationtype as any other amount will cause an error. Thanks for any help in advance.

Edit: I have checked my code with shellcheck.net, and I believe that there are no syntax based mistakes. I need help not really with the specifics of the code but rather what the best way to achieve the result I want, with the rough idea that I have highlighted above. Is this the best way to go about this? How would I get the operationtype based helpmenu functionality? And why isn't getopts running? Sorry for any confusion, I shall remove the pastebin link as making you read through it was not my intention at all.


Solution

  • The main difference between the ASK in the question and the 'traditional' usage of getopt is that the command line arguments follows the structure

    script sub-command [options] arguments
    

    Where the 'traditional' use case for getopt expects the following (and the command may be implied from the options, or be part of the argument list).

    script [options] arguments
    

    Small minor changes are needed here, hinting to getopt that the first command line arguments should be ignored. Two options: shift the argument list, or update the OPTIND to skip those arguments

    # SHIFT the command outside the argument list
    operationtype=$1
    shift
    if [[ operationtype == "Operation1" ]]; then
       # Execute the operation 1
       ...
       while getopts ":hs:d:m:f:a" opt; do
       ...
       done
    

    Or updating OPTIND to skip 1 the first argument.

    operationtype=$1
    OPTIND=$((OPTIND+1))
    if [[ operationtype == "Operation1" ]]; then
       # Execute the operation 1
       ...
       while getopts ":hs:d:m:f:a" opt; do
       ...
       done
    

    Note: Each of those solution can be used to provide 2 sets of options. One for the script, and one for the sub-command. For example, the docker command line:

    docker [options] sub-command [sub-command options]
    

    Original Answer:

    Completely revised to to address changes made by the OP.