How to make shell script arguments optional?

Here is my script:

#!/bin/ksh

usage ()
{
     echo " Usage: $0 <opt1> <opt2> <opt3> <opt4>"
}

if [ $# -lt 3 ]; then
        usage
        exit;
fi

prog -a $1 -b $2 -c $3 -d $4 2>&1 | tee -a ~/$1.log

I want argument 4 to be optional, so if there's no argument for opt4, that it doesn't execute the -d argument in the actual program called 'prog'

Try:

#!/bin/ksh

usage ()
{
     echo " Usage: $0 <opt1> <opt2> <opt3> <opt4>"
}

if [ $# -lt 3 ]; then
        usage
        exit;
fi

if [ -z $4 ]; then
  prog -a $1 -b $2 -c $3 2>&1 | tee -a ~/$1.log
else
  prog -a $1 -b $2 -c $3 -d $4 2>&1 | tee -a ~/$1.log
fi
1 Like

Or the shorter:

#!/bin/ksh

usage ()
{
     echo " Usage: $0 <opt1> <opt2> <opt3> [<opt4>]"
}

[[ $# -lt 3 ]] && { usage; exit; }

prog -a $1 -b $2 -c $3 ${4:+-d $4} 2>&1 | tee -a ~/$1.log
1 Like

@bartus11
Would it be safer to put double quotes around the $4?:

...
if [ -z "$4" ]; then
...

@jlliagre
What exactly does "${4:+-d $4}" mean? Can this be applied to the other arguments as well, to make them optional?

Thanks

From ksh manual page (but this applies to all posix shells):

${parameter:+word}

       If  parameter  is  set  and is non-null, substitute word. Otherwise
       substitute nothing.

Here, "word" is "-d $4"

Yes.

This would require at least 3 arguments though, correct? Because if you only provided 2, the script wouldn't know which 2 arguments they are for and most likely use the first two options. Right? Or can this be circumvented somehow?

Here is generic solution. Argument order is not fixed and so on.

usage ()
{
     echo " Usage: $0 -a value -b value -c value [ -d value ]" >&2
     exit 1
}

#####################
a=""
b=""
c=""
d=""
# parse options
while [ $# -gt 0 ]
do
        case "$1" in
                -a) a="$2" ; shift ;;
                -b) b="$2" ; shift ;;
                -c) c="$2" ; shift ;;
                -d) d="$2" ; shift ;;
                --) shift; break ;;
                -*) usage ;;
                *) break ;;  # arguments ...
        esac
        shift
done
# now $* include arguments
[ "$a" = "" ] && usage
[ "$b" = "" ] && usage
[ "$c" = "" ] && usage

if [ "$d" = "" ] ; then
      prog -a "$a" -b "$b" -c "$c" 2>&1 | tee -a ~/"$a".log 
else
     prog -a "$a" -b "$b" -c "$c" -d "$d" 2>&1 | tee -a ~/"$a".log
fi

I used to check the $# first and proceed accordingly.

No, all arguments can be optional.

The arguments are positional so the shell definitely knows which ones they are.

Yes, you can just pass empty arguments. The corresponding options would then be ignored.