[Solved] Read and validate input arguments

Hi,
I need to get input arguments, as well as validate them. This is how I'm reading them:

 
#!/bin/bash
args="$@" # save arguments to variable 
## Read input arguments, if so 
while [ $# -ge 1 ]; do 
    case $1 in 
        -v | --verbose ) verbose=true;; 
        -z | --gzip ) compression="gz";; 
        -b | --bzip2 ) compression="bz";; 
        -n | --no-compress) compression="none";;
        -h | --help ) showUsage; exit 0 ;; 
        *) echoErr "Invalid option $1. Use -h or --help to show usage"; exit 1 ;; 
    esac
    shift
done

My problem is how validate incompatible arguments. For instance, it's not allowed to call script with both "-z" and "-b", or "-n" and "-z", and so on.
I stored input arguments into a variable called "args" (because shift command unsets them).
Is there a simple way to validate other than a double loop? This is what occurred to me, but is really ugly. Any hint to improve?

args="$@" # store into args variable
# [...] previous while
argsIterate=($args) # I don't know how to iterate over $args, except convert into an array
for (( i=0;i<${#argsIterate[@]};i++)); do # 1st loop over all arguments
    for (( j=i+1;j<${#argsIterate[@]};j++)); do # 2nd loop over rest arguments
        if ([ "${argsIterate[${i}]}" == "-z" ] && ([ "${argsIterate[${j}]}" == "-b" ] || [ "${argsIterate[${j}]}" == "-n" ])) || \
                ([ "${argsIterate[${i}]}" == "-b" ] && ([ "${argsIterate[${j}]}" == "-z" ] || [ "${argsIterate[${j}]}" == "-n" ])) || \
                ([ "${argsIterate[${i}]}" == "-n" ] && ([ "${argsIterate[${j}]}" == "-z" ] || [ "${argsIterate[${j}]}" == "-b" ])) ; then 
            echo "Incompatible arguments ${argsIterate[${i}]} and ${argsIterate[${j}]}"
            exit -1; 
        fi
    done
done

Thanks and sorry for my english

Albert.

Saving args to a variable like that is going to break down if any of your filenames have spaces or the like.

Just do a little more checking in your input loop:

#!/bin/bash

die() {
        echo "$@" >&2
        exit 1
}

## Read input arguments
while [ $# -ge 1 ]; do 
    case "$1" in 
        -v | --verbose ) verbose=true;; 
        -z | --gzip )
                [ -z "$compression" ] || die "Conflicting option $1"
                compression="gz";; 
        -b | --bzip2 )
                [ -z "$compression" ] || die "Conflicting option $1"
                compression="bz";; 
        -n | --no-compress)
                [ -z "$compression" ] || die "Conflicting option $1"
                compression="none";;
        -h | --help ) showUsage; exit 0 ;; 
        *) die "Invalid option $1. Use -h or --help to show usage" ;; 
    esac
    shift
done
1 Like

It's not what I expected, but it's a nice and simple solution. Did not know why it didn't occur to me... I was stubborn in use an "args" variable, that I didn't realize there was a much simpler solution :wink:

Thanks a lot.

Albert.

The solution of Corona will indeed work, but i think there is an even more "correct" solution to this: use the getopts keyword of your shell or the /usr/bin/getopts executable respectively.

getopts provides (basic) error handling and essentially does what you want to achieve, plus it understands the common UNIX-syntax: if you have "/some/command" and want to pass it two options, "x" and "y" you would write:

/some/command -xy

rather than

/some/command -x -y

Corona688s way of parsing the commandline would require the latter instead of the former. You might read the man page of getopts and ask again if you have any additional questions about its usage.

Here is a (very basic) example of how to use it: suppose you have 2 legal options, "-a" and "-b", of which one takes an additional argument:

#! /bin/ksh
typeset aflag=""
typeset bflag=""
typeset bval=""
typeset curropt=""
typeset -i OPTIND=0       # number of passed args

while getopts ab: curropt ; do
     case $curropt in
          a)
               aflag=1
               ;;

          b)
               bflag=1
               bval="$OPTARG"
               ;;

         ?)
               printf "Usage: %s: [-a] [-b value] args\n" $0
               exit 2
               ;;
     esac
done

if [ ! -z "$aflag" ] ; then
     printf "Option -a specified\n"
fi

if [ ! -z "$bflag" ]; then
     printf 'Option -b "%s" specified\n' "$bval"
fi

shift $(($OPTIND -1))           # clear cmdline
exit 0

I hope this helps.

bakunin

1 Like