I'm trying to build up a shell script that accepts various options and getopts
seems like a good solution as it can handle the variable ordering of the options and arguments (I think!).
I'll only be using short options and each short options will require a corresponding value eg: ./command.sh -a arga -g argg -b argb
but I would like to allow the options to be entered in a non-specific order, as is the way most people are accustomed to working with shell commands.
The other point is that I would like to do my own checking of option argument values, ideally within the case
statements. The reason for this is that my testing of :)
in my case
statement has yielded inconsistent results (probably through lack of understanding on my part).
For example:
#!/bin/bash
OPTIND=1 # Reset if getopts used previously
if (($# == 0)); then
echo "Usage"
exit 2
fi
while getopts ":h:u:p:d:" opt; do
case "$opt" in
h)
MYSQL_HOST=$OPTARG
;;
u)
MYSQL_USER=$OPTARG
;;
p)
MYSQL_PASS=$OPTARG
;;
d)
BACKUP_DIR=$OPTARG
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 2;;
:)
echo "Option -$OPTARG requires an argument" >&2
exit 2;;
esac
done
shift $((OPTIND-1))
echo "MYSQL_HOST='$MYSQL_HOST' MYSQL_USER='$MYSQL_USER' MYSQL_PASS='$MYSQL_PASS' BACKUP_DIR='$BACKUP_DIR' Additionals: $@"
Was failing for occurrences like this… ./command.sh -d -h
When I want it to flag -d as requiring an argument but I get the value of -d=-h
which is not what I need.
So I figured it would be easier to run my own validation within the case statements to ensure that each option is set and set only once.
I'm trying to do the following but my if [ ! "$MYSQL_HOST" ]; then
blocks are not triggered.
OPTIND=1 # Reset if getopts used previously
if (($# == 0)); then
echo "Usage"
exit 2
fi
while getopts ":h:u:p:d:" opt; do
case "$opt" in
h)
MYSQL_HOST=$OPTARG
if [ ! "$MYSQL_HOST" ]; then
echo "host not set"
exit 2
fi
;;
u)
MYSQL_USER=$OPTARG
if [ ! "$MYSQL_USER" ]; then
echo "username not set"
exit 2
fi
;;
p)
MYSQL_PASS=$OPTARG
if [ ! "$MYSQL_PASS" ]; then
echo "password not set"
exit 2
fi
;;
d)
BACKUP_DIR=$OPTARG
if [ ! "$BACKUP_DIR" ]; then
echo "backup dir not set"
exit 2
fi
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 2;;
#:)
# echo "Option -$opt requires an argument" >&2
# exit 2;;
esac
done
shift $((OPTIND-1))
echo "MYSQL_HOST='$MYSQL_HOST' MYSQL_USER='$MYSQL_USER' MYSQL_PASS='$MYSQL_PASS' BACKUP_DIR='$BACKUP_DIR' Additionals: $@"
Is there a reason that I'm unable to check if an OPTARG
has zero-length from within getopts ... while ... case
?
What's the better way to run my own argument validation with getopts
in a case where I don't want to be relying on the :)
. Perform my argument validation outside of the while ... case ... esac
?
Then I could end up with argument values of -d
etc and not catching a missing option.
Best Answer
When you call your second script (I saved it as
getoptit
) with:This will print:
So BACKUP_DIR is set, and you are testing with
if [ ! "$BACKUP_DIR" ]; then
if it is not set, so it is normal that the code inside of it is not triggered.If you want to test if each option is set once, you have to do that before you do the assigment from the $OPTARG value. And you should probably also check for the $OPTARG to start with a
'-'
(for the-d -h
error) before assigning: