Not sensibly with Bash's/POSIX getopts
, but you could do it with the "enhanced" getopt
(without an s) from util-linux or Busybox. (And those only, in particular many "traditional" getopt
implementations are broken wrt. whitespace also)
The man page says of getopts
:
optstring contains the option characters to be recognized; if a character is followed by a colon, the option is
expected to have an argument, which should be separated from it by white space.
there's no mention of optional option-arguments.
Of course you could have another optional option to give the non-default value. E.g. let -n
take no argument and just enable nice things, and let -N <arg>
take the argument, enable nice things and set the value.
E.g. something like this:
#!/bin/bash
HAS_NICE_THINGS=0
NICE_THINGS_VALUE=50
while getopts nN: option; do
case "${option}" in
n)
HAS_NICE_THINGS=1
shift;;
N)
HAS_NICE_THINGS=1
NICE_THINGS_VALUE=$OPTARG
shift; shift;;
esac
done
if [ "$HAS_NICE_THINGS" = 1 ]; then
echo "nice things enabled with value $NICE_THINGS_VALUE"
fi
would give
$ bash nice-getotps.sh -n
nice things enabled with value 50
$ bash nice-getopts.sh -N42
nice things enabled with value 42
The util-linux getopt
takes optional option-arguments with the double-colon syntax. It's a bit awkward to use, and you need to mess with eval
, but done correctly, it seems to work.
Man page:
-o shortopts
[...] Each short option character in shortopts may be followed by one colon to indicate it has a required argument, and by two colons to indicate it has an optional argument.
With a script to just print the raw values so we can check it works properly (getopt-optional.sh
):
#!/bin/bash
getopt -T
if [ "$?" -ne 4 ]; then
echo "wrong version of 'getopt' installed, exiting..." >&2
exit 1
fi
params="$(getopt -o an:: -- "$@")"
eval set -- "$params"
while [ "$#" -gt 0 ]; do
case "$1" in
-n)
echo "option -n with arg '$2'"
shift 2;;
-a)
echo "option -a"
shift;;
--)
shift
break;;
*)
echo "something else: '$1'"
shift;;
esac
done
echo "remaining arguments ($#):"
printf "<%s> " "$@"
echo
we get
$ bash getopt-optional.sh -n -a
option -n with arg ''
option -a
remaining arguments (0):
<>
$ bash getopt-optional.sh -n'blah blah' -a
-n 'blah blah' -a --
option -n with arg 'blah blah'
option -a
remaining arguments (0):
<>
No argument to -n
shows up as an empty argument.
Not that you could pass an explicit empty argument anyway, since the option-argument needs to be within the same command line argument as the option itself, and -n
is the same as -n""
after the quotes are removed. That makes optional option-arguments awkward to use in that you need to use -nx
, as -n x
would be taken as the option -n
(without an opt-arg), followed by a regular non-option command line argument x
. Which is unlike what would happen if -n
took a mandatory option-argument.
More about getopt
on this Stackoverflow answer to How do I parse command line arguments in Bash?
Note that that appears limited to that particular implementation of getopt
, one that happens to be common on Linux systems, but probably not on others. Other implementations of getopt
might not even support whitespace in arguments (the program has to do shell quoting for them). The "enhanced" util-linux version has the -T
option to test if you have that particular version installed. There's some discussion on the limitations and caveats with getopt
here:
getopt, getopts or manual parsing - what to use when I want to support both short and long options?
Also, getopt
is not a standard tool like getopts
is.
-n
takes a value, what should happen if the user passes-n
with no value? Should it behave as though-n
were not passed? Should-n
result in one value forNICE_THINGS
while running without-n
should result in another value? Please [edit] your question and explain what each scenario is. – terdon Aug 08 '21 at 13:16NICE_THINGS
, which would be used with just plain-n
(with no value). – ilkkachu Aug 08 '21 at 13:36-i
option. In contrast to e.g. the FreeBSD version where the option-argument is mandatory. – ilkkachu Aug 09 '21 at 09:26-i
option tosed
is a good example of a hideous design decision, because virtually no GNU user knows what the option is actually for (it's for setting a backup suffix) and it's hopelessly non-portable. – Kusalananda Aug 09 '21 at 09:30sed -i
, come on, why would GNU users in particular be unfamiliar with it, and not sed users in general? But sure, it's not POSIX, though seems to be mentioned in at least the GNU, FreeBSD, OpenBSD and Busybox versions of sed. ...and apparently OpenBSD and Busybox also take it with an optional opt-arg. (And yes, I meant both of you mods.) – ilkkachu Aug 09 '21 at 09:42-t jpg
for the type and-q 99
for the quality. But even if you end up doing that, it doesn't invalidate the question about optional opt-args. (well, not as far as I can see anyway.) – ilkkachu Aug 15 '21 at 16:39-j
flag, and then tweak the quality if necessary (run script again if image is too bad or large). But I didn't expect this to be such a complex problem. – WoodrowShigeru Aug 15 '21 at 19:38