I don't see why you couldn't simulate long options by treating -
as a short option requiring an argument:
$ cat /tmp/foo
while getopts :aq:-: opt; do
case $opt in
a) echo option -a;;
q) echo option -q with value "$OPTARG";;
-) case $OPTARG in
abc) echo option --abc;;
def=*) echo option --def with value "${OPTARG#*=}";;
*) echo >&2 "unknown option --$OPTARG";;
esac;;
:) echo >&2 "-$OPTARG needs an argument";;
*) echo >&2 "unknown option -$OPTARG";;
esac
done
shift "$((OPTIND - 1))"
echo argv: "$@"
$ sh /tmp/foo -a -qt --abc --def=ghi -- foo bar
option -a
option -q with value t
option --abc
option --def with value ghi
argv: foo bar
If you want to make a list of arguments (eg. to call another command with it), and your shell doesn't support arrays (eg. debian's or busybox's /bin/sh
), you can use the following trick, which will pass to eval a list of argument indexes instead of actual strings; that will avoid any IFS splitting/globbing/whitespace annoyances:
$ cat /tmp/foo
# handle -a and -qval
# call another command with any long options and non-option arguments
av=
while getopts :aq:-: opt; do
case $opt in
a) echo option -a;;
q) echo option -q with value "'$OPTARG'";;
-) av="$av \"\${$((OPTIND-1))}\"" ;; # pass long options unchanged
:) echo >&2 "-$OPTARG needs an argument";;
*) echo >&2 "unknown option -$OPTARG";;
esac
done
i=$OPTIND; while [ "$i" -le "$#" ]; do av="$av \"\${$i}\"" i=$((i + 1)); done
print_its_args(){ for a; do printf ' {%s}' "$a"; done; echo; }
echo "print_its_args $av"
eval "print_its_args $av"
Then:
$ sh /tmp/foo -aqval --foo='a ** b' --abc -- '(moo)'
option -a
option -q with value 'val'
print_its_args "$2" "$3" "$5"
{--foo=a ** b} {--abc} {(moo)}
This trick could be used in other situations; but this is a case where a simpler solution like set -- "$@" arg
to push args into $@
cannot be used, because modifying $@
inside the getopts
loop cannot be done in a portable way.
my_utility
, but it requires keeping in mind special cases where the getopts parameter must handle options with values. – Thomas Dickey Nov 16 '18 at 00:46shutil.copy2()
for the former andsubprocess.check_call([...])
andsubprocess.check_output([...])
for the latter. In many cases you can still keep these as one-liners. And the language expressiveness you gain for the rest of the script is definitely worth it! – filbranden Nov 16 '18 at 02:08