Calling different programs with different options

2019-09-06 02:20发布

问题:

I am trying to make a script that can execute a different number of programs simultaneously (prog1, prog2,...prog5, or even more).

It can be prog1 only , or prog1&prog2, or prog1&prog2&prog3, prog1&prog3&prog4&prog6&prog8

Each program can be called with different options and will execute different things depending on it's calling options.

For instance prog1 can be called in these different ways:

  1. prog1 -d "-f 10 -g 20" (-d is an option and "-f 10 -g 20" arguments for that specific option)
  2. prog1 -B
  3. prog1 -R "-l 2 -m 9"
  4. prog1 -S "10 20 30"

It can also be called with its combinations like: prog1 -d "-f 10 -g 20" -B -R "-l 2 -m 9"

I decided to use a two stage getopt (nested getopt) to cope with this problem.

In the first stage getopt will handle the different programs that the user gives as input to the script. In the second stage the getopt will handle the different options and arguments that each program can get.

My script is the following:

#!/bin/bash

args=`getopt -o :v -l prog1:,prog2,prog3,prog4,prog5 -- "$@"`  # Here prog2,prog3..prog5 do not have a colon (:) to save some space but all of them will eventually get since when they are called they should have input arguments   
eval set -- "$args"
while true; do
case $1 in

      -v ) echo "VERBOSE"; shift;;
 --prog1 )
             echo "prog1 has been chosen with the following option:$2"

             args=`getopt -o :d:BR:S: -- "$@"`
               eval set -- "$args"
                 while true; do
                    case $1 in

                        -d )  echo "-d:$2"; shift 2;;
                        -B )  echo "-B"; shift;;
                        -R )  echo "-R:$2" ;shift 2 ;;
                        -S )  echo "-S:$2"; shift 2;;
                         --)  shift; break;; 
                     esac
                  done

             shift 2;; 

--prog2 )
             echo "similar things as in prog1";  shift
               ;;                           
--prog3 ) 
            echo "similar things as in prog1";  shift
               ;; 

--prog4 )
             echo "similar things as in prog1";  shift
               ;;                           
--prog5 ) 
            echo "similar things as in prog1";  shift
               ;;                         

     -- ) 
              shift; break;;
esac
done

when I run the script as:my_script --prog1 -d "-f 10 -g 100" it is able to get the -d argument but somehow it gets in an infinite loop and at the same time is not able to get the other argument "-f 10 -g 100"

Ideally the script should run with calls such as :

  1. my_script --prog1 -d "-f 10 -g 100" -R "-l 2 -m 9" --prog3 -M "-r 9 0.5 6" -F "-o 2 -q- 4" --prog5 .... etc

  2. my_script --prog1 -d "-f 10 -g 100".

  3. my_script --prog1 -d "-f 10 -g 100" -B --prog3 -F "-o 2 -q- 4"

  4. my_script --prog3 -F "-o 2 -q- 4" -M --prog2 -G

  5. etc

Do you have any ideas on how to overcome this problem?

Thank you in advance

回答1:

Your problem is in part that you set the shell's current argument list twice:

args=$(getopt -o :v -l prog1:,prog2,prog3,prog4,prog5 -- "$@")
eval set -- "$args"  # First set of arguments
while true; do
case $1 in
      -v ) echo "VERBOSE"; shift;;
 --prog1 )
             echo "prog1 has been chosen with the following option:$2"
             args=`getopt -o :d:BR:S: -- "$@"`
             eval set -- "$args"    # Second set of arguments blows away the first

The second set of options blows away the first set.

The cause of the 'stuck in a loop' problem is less obvious.

It is going to be hard to fix this. I think you will have to have a loop to process the program level options; then, for each program that is to be executed, you will need a new loop after (not nested within) the first option processing loop:

args=$(getopt -o :v -l prog1:,prog2,prog3,prog4,prog5 -- "$@")
eval set -- "$args"
while true
do
    case $1 in
          -v ) echo "VERBOSE"; shift;;
     --prog1 )
               echo "prog1 has been chosen with the following options: $2"
               opts1="$2"
               shift 2;;
     --prog2 )
               echo "prog2 has been chosen with the following options: $2"
               opts2="$2"
               shift 2;;
     --prog3 )
               echo "prog3 has been chosen with the following options: $2"
               opts3="$2"
               shift 2;;
     --prog4 )
               echo "prog4 has been chosen with the following options: $2"
               opts4="$2"
               shift 2;;
     --prog5 )
               echo "prog5 has been chosen with the following options: $2"
               opts5="$2"
               shift 2;;
          -- ) 
               shift; break;;
    esac
done

if [ -n "$opts1" ]
then
     args=`getopt -o :d:BR:S: -- "$opts1"`
     eval set -- "$args"
     while true
     do
         case $1 in
         -d )  echo "-d:$2"; shift 2;;
         -B )  echo "-B"; shift;;
         -R )  echo "-R:$2" ;shift 2 ;;
         -S )  echo "-S:$2"; shift 2;;
         -- )  shift; break;; 
         esac
     done
     # Now execute prog1?
fi

# And similarly for $opts2 .. $opts5.

SSCCE

Here's working code for running two programs. The command options are ruthlessly parallel, but they're different too. One of the key changes is setting the 'per program' options from the appropriate saved option strings. It is cut down to handle just two programs. My version of GNU getopt is not installed ahead of the system getopt, hence the variable to define which getopt program to run.

GETOPT=/usr/gnu/bin/getopt

args=$($GETOPT -o :v -l prog1:,prog2: -- "$@")
eval set -- "$args"

while [ $# -gt 0 ]
do
    case $1 in
          -v ) echo "VERBOSE"; shift;;
     --prog1 )
               echo "prog1 has been chosen with the following options: $2"
               opts1="$2"
               shift 2;;
     --prog2 )
               echo "prog2 has been chosen with the following options: $2"
               opts2="$2"
               shift 2;;
          -- ) 
               shift; break;;
           * ) echo "$0: oops! $1 unexpected" >&2; exit 1;;
    esac
done
echo "$0: residual arguments: $@"

if [ -n "$opts1" ]
then
     args=$($GETOPT -o d:BR:S: -- $opts1)
     eval set -- "$args"
     while [ $# -gt 0 ]
     do
         case $1 in
         -d )  echo "prog1 -d:$2"; shift 2;;
         -B )  echo "prog1 -B";    shift 1;;
         -R )  echo "prog1 -R:$2"; shift 2;;
         -S )  echo "prog1 -S:$2"; shift 2;;
         -- )  shift; break;; 
          * )  echo "$0: oops processing prog1 - $1 unexpected" >&2; exit 1;;
         esac
     done
     echo "prog1 opts -- $@"
     # Now execute prog1?
fi

if [ -n "$opts2" ]
then
     args=$($GETOPT -o f:AT:G: -- $opts2)
     eval set -- "$args"
     while [ $# -gt 0 ]
     do
         case $1 in
         -f )  echo "prog2 -f:$2"; shift 2;;
         -A )  echo "prog2 -A";    shift 1;;
         -T )  echo "prog2 -T:$2"; shift 2;;
         -G )  echo "prog2 -G:$2"; shift 2;;
         -- )  shift; break;; 
          * )  echo "$0: oops processing prog2 - $1 unexpected" >&2; exit 1;;
         esac
     done
     echo "prog2 opts -- $@"
     # Now execute prog2?
fi

Example run

$ sh multiopts.sh -v --prog2 "-f file -A -G garbage -T tag -- zero one" \
>                    --prog1="-S silence -R rubbish -B -d dog -- aleph null" -- abc def
VERBOSE
prog2 has been chosen with the following options: -f file -A -G garbage -T tag -- zero one
prog1 has been chosen with the following options: -S silence -R rubbish -B -d dog -- aleph null
multiopts.sh: residual arguments: abc def
prog1 -S:silence
prog1 -R:rubbish
prog1 -B
prog1 -d:dog
prog1 opts -- aleph null
prog2 -f:file
prog2 -A
prog2 -G:garbage
prog2 -T:tag
prog2 opts -- zero one
$