I am parsing options with getopts
but would like to handle long-options as well.
print-args ()
{
title="$1" ; shift
printf "\n%s\n" "${title}: \$@:"
for arg in "$@"; do
(( i = i + 1 ))
printf "%s |%s|\n" "${i}." "$arg"
done
}
getopts_test ()
{
aggr=()
for arg in "$@"; do
case $arg in
("--colour"|"--color") aggr+=( "-c" ) ;;
("--colour="|"--color=") aggr+=( "-c" "${arg#=}" ) ;;
() aggr+=( "$arg" ) ;;
esac
done
print-args "print" "$@"
eval set -- "${aggr[@]}"
print-args "eval" "$@"
set -- "${aggr[@]}"
print-args "set" "$@"
local OPTIND OPTARG
local shortopts="C:"
while getopts "$shortopts" arg; do
case $arg in
("c") context="$OPTARG" ;;
(*) break ;;
esac
done
shift $(( OPTIND - 1 ))
}
But I wonder whether the use of set -- "${aggr[@]}"
is correct.
Or is the following (using eval
) more appropriate?
eval set -- "${aggr[@]}"
I have performed a test shown below. With eval, the string "Gunga Din" is split up, whereas with set -- "${aggr[@]}"
, it is being parsed correctly as a single string.
getopts_test -f -g 130 --colour="170 20" "Gunga Din"
print: $@:
- |-f|
- |-g|
- |130|
- |--colour=170 20|
- |Gunga Din|
eval: $@:
- |-f|
- |-g|
- |130|
- |-c|
- |170|
- |20|
- |Gunga|
- |Din|
set: $@:
- |-f|
- |-g|
- |130|
- |-c|
- |170 20|
- |Gunga Din|
Then I ran another function that uses the non-GNU getopt
.
getopt_test ()
{
shortopts="Vuhv::H::w::e::n::l::C:"
shortopts="${shortopts}bgcrmo"
longopts="version,usage,help,verbosity::"
longopts="${longopts},heading::,warning::,error::"
longopts="${longopts},blu,grn,cyn,red,mgn,org"
opts=$( getopt -o "$shortopts" -l "$longopts" -n "${0##*/}" -- "$@" )
print-args "$@:" "$@"
print-args "opts:" "$opts"
set -- "$opts"
print-args "set -- "$opts"" "$@"
eval set -- "$opts"
print-args "eval set -- "$opts"" "$@"
}
This resulted in the following
getopt_test --warning=3 "foo'bar" "Gunga Din"
$@:
- |--warning=3|
- |foo'bar|
- |Gunga Din|
opts:
- | --warning '3' -- 'foo'''bar' 'Gunga Din'|
set -- "$opts"
- | --warning '3' -- 'foo'''bar' 'Gunga Din'|
eval set -- "$opts"
- |--warning|
- |3|
- |--|
- |foo'bar|
- |Gunga Din|
As shown the result of getopt is a single entry with positional arguments re-arranged. This shows the need to use eval set -- "$opts"
to split the positional arguments in the opts
string into five entries for option parsing and processing.
getopt
tool? It'll handle quite a lot of this for you. (Here,getopt --version
→getopt from util-linux 2.33.1
) – Chris Davies Oct 30 '21 at 10:42util-linux
is not part of the GNU project. – Stéphane Chazelas Oct 30 '21 at 14:04getopt
was GNU – Chris Davies Oct 30 '21 at 17:33