问题
I am using a standard getopts logic. But I want to how I can make the options I offer- mutually exclusive. e.g.
shell.sh -a SID
<accepted>
shell.sh -b SID
<accepted>
shell.sh -ab SID
Message- using ab together is the same as running shell.sh without any options supplying just SID . Help usage < ya da ya >
shell.sh
Please enter SID at the minimum. Usage < ya da ya >
shell.sh SID
<accepted>
I am trying to develop this logic using something like below
while getopts ":a:b:" opt; do
case $opt in
a ) SID="$OPTARG";;
set var=1
b ) SID="$OPTARG";;
set var=2
\?) echo "Invalid option: -"$OPTARG"" >&2
exit 1;;
) echo "Option -"$OPTARG" requires an argument." >&2
exit 1;;
esac
done
If (( val == 1 )) then ; # option a is invoked SID supplied
<stuff>
elif (( val == 2 )) then ; # option b is invoked SID supplied
<stuff>
else # SID supplied but neither a or b is invoked
<stuff>
fi
How do enforce mutually exclusive flags. I a sure there are more acrobat ways to do it. I think I am missing something commonsense here - and trying to figure that out . Thx
$ more opt.ksh
die () {
echo "ERROR: $*. Aborting." >&2
return 1
}
var=
while getopts ":a:b:" opt; do
case $opt in
a ) SID="$OPTARG"
[ "$var" = 2 ] && die "Cannot specify option a after specifying option b"
[ "$OPTARG" = b ] && die "Do not specify b as a value for option a"
var=1
;;
b ) SID="$OPTARG"
[ "$var" = 1 ] && die "Cannot specify option b after specifying option a"
[ "$OPTARG" = a ] && die "Do not specify a as a value for option b"
var=2
;;
:) die "Must supply an argument to $OPTARG"
;;
\?) die "Invalid option: -$OPTARG. Abort"
;;
esac
done
shift $(($OPTIND - 1))
[ "$SID" ] || SID=$1
[ "$SID" ] || die "You must, at the minimum, supply SID"
I am using ksh
$ ps -p $$
PID TTY TIME CMD
1261 pts/45 00:00:00 ksh
1st time I run it .
$ . opt.ksh -a 123 -b 123 # c0
ERROR: Cannot specify option b after specifying option a. Aborting.
-bash: /home/d1ecom1/gin1: No such file or directory
$ . opt.ksh -ab 123 # c1 should reject "cant use a and b togather. try with a or b"
-nologin: .[25]: shift: 4: bad number
$ . opt.ksh -a -b # c2 same as c1's message
$ . opt.ksh -a -b 123 # c3 same as c1
$ . opt.ksh -a -b 123 # c5
$ . opt.ksh -ab 123 # c6
$ . opt.ksh -a 123 -b 123 # c7
All above cases C0:C7 should reject. Notice C0 and C7 are the same. Yet inspite of this C0 gives the expected error and C7 will not give any error ? strange
only ones to work should be
. opt.ksh -a 123
. opt.ksh -b 123
. opt.ksh 123
@hvd :TYSM for your reply. I would like to add an additonal flag -p that will give a "path override" option. so maybe we have to extend getopt to take parameters like this
die () {
echo "ERROR: $*. Aborting." >&2
exit 1
}
var=
opta=false
optb=false
while getopts ":ab:p" opt; do
case $opt in
a ) $optb && die "Cannot specify option a after specifying option b"
opta=true
;;
b ) $opta && die "Cannot specify option b after specifying option a"
optb=true
;;
p ) [ -d "$mypath" ] && die "path is invalid"
;;
\?) die "Invalid option: -$OPTARG. Abort"
;;
esac
done
shift $(($OPTIND - 1))
test $# -eq 0 && die "You must supply SID"
test $# -eq 1 || die "Too many command-line arguments"
# SID=$1
The above was the old approach. Now I have 2 Input parameters. One is the SID and the other is the path. Is it as simple as the above or do I need to add more checks to prevent other unwanted combinations. The question I guess I am trying to aim at is , what more provisions would need to be made to allow this -p parameter which is an optiona override parameter. - p can co-exist with any parameter above but then I guess one requirement is that it should be immediately following the -p flag so this should not allow - because its not clear .
shell.sh -b -p 123 /home/yadaya
Thanks again
回答1:
Here's how you can do it without making SID an option argument, which makes more sense to me:
die () {
echo "ERROR: $*. Aborting." >&2
exit 1
}
var=
opta=false
optb=false
while getopts ":ab" opt; do
case $opt in
a ) $optb && die "Cannot specify option a after specifying option b"
opta=true
;;
b ) $opta && die "Cannot specify option b after specifying option a"
optb=true
;;
\?) die "Invalid option: -$OPTARG. Abort"
;;
esac
done
shift $(($OPTIND - 1))
test $# -eq 0 && die "You must supply SID"
test $# -eq 1 || die "Too many command-line arguments"
SID=$1
The changes I've made are:
return 1
becomesexit 1
(unrelated to your question, butdie
sounds like it shouldn't continue).return 1
would merely make thedie
function return non-successfully, but the calls todie
don't check its result, it would carry on regardless.- The
getopts
string is:ab
instead of:a:b:
. No matter how you call your script, you always need to pass exactly one SID, regardless of options, so it doesn't make sense to me to include it as part of the options. - The options are stored in boolean
opta
andoptb
variables for easier checking - After all options are passed, the remaining command-line arguments are counted. Unless it's exactly one, the call to the script is invalid.
All your valid calls in the question are accepted, all the invalid ones are rejected. One note of interest about this, though: your test case c7 (-a 123 -b 123
) is expected to fail, and does fail, but not because -a
and -b
are combined. Instead, in my approach, since -b
appears after a non-option argument, it itself is a non-option argument, and the reason for rejecting it becomes "Too many command-line arguments".
回答2:
This makes options a and b mutually exclusive:
die () {
echo "ERROR: $*. Aborting." >&2
exit 1
}
var=
while getopts ":a:b:" opt; do
case $opt in
a ) SID="$OPTARG"
[ "$var" = 2 ] && die "Cannot specify option a after specifying option b"
[ "$OPTARG" = b ] && die "Do not specify b as a value for option a"
var=1
;;
b ) SID="$OPTARG"
[ "$var" = 1 ] && die "Cannot specify option b after specifying option a"
[ "$OPTARG" = a ] && die "Do not specify a as a value for option b"
var=2
;;
:) die "Must supply an argument to $OPTARG"
;;
\?) die "Invalid option: -$OPTARG. Abort"
;;
esac
done
shift $(($OPTIND - 1))
[ "$SID" ] || SID=$1
[ "$SID" ] || die "You must, at the minimum, supply SID"
Note that optargs interprets shell.sh -ab SID
as option a
with argument b
followed by SID
as an argument to the script, not part of an option. To detect that problem, the lines above involving [ "$OPTARG" = b ] && die...
and [ "$OPTARG" = a ] && die...
were added.
You wanted to accept shell.sh SID
with no option specified. This is handled by the first two lines after the case statement.
回答3:
For a case where you must specify exactly one of options -s
, -i
or -h
, and where you have two more options -v
and -n
which are optional, you could do something like the following:
modes=0
while getopts :vs:i:h:n opt; do
case "$opt" in
v)
verbose='non-empty-string-true-flag'
;;
s)
((modes++))
: do something with OPTARG, the s form
;;
i)
((modes++))
: do something with OPTARG, the i form
;;
h)
((modes++))
: do something with OPTARG, the h form
;;
n)
dryrun='non-empty-string-true-flag'
;;
:)
printf 'Option -%s requires an argument\n' "$OPTARG" >&2
usage
exit 1
;;
*)
printf 'Invalid option: -%s\n' "$OPTARG" >&2
usage
exit 1
;;
esac
done
shift "$((OPTIND-1))"
if [ "$modes" -eq 0 ]; then
printf "Missing required option\n" >&2
usage
exit 1
elif [ "$modes" -gt 1 ]; then
printf "Error: -h, -i and -s are mutually exclusive and may only be used once\n" >&2
usage
exit 1
fi
This is excerpted from a script of mine, with the processing to be done on the s
, i
or h
arguments omitted for simplicity of illustration.
The synopsis for the script looks like:
$0 [ -v ] [ -n ] { -i IPADDRESS | -h HOSTNAME | -s HOSTSHA }
来源:https://stackoverflow.com/questions/21695565/how-to-use-mutually-exclusive-flags-in-your-shell-and-add-an-optional-argument-f