This is bad code. getopt is pretty much unusable¹. Use the getopts shell builtin instead (it has two advantages: it can work, and it's available on all POSIX platforms).
What args=$(getopt optstring $*); set -- $args does is:
- Split each command line argument at whitespace and replace it by the list of whitespace-delimited words.
- Take each of the resulting word and interpret it as a wildcard pattern. If the pattern matches at least one file, replace the pattern by the list of matching files.
- Pass the resulting list of words to the
getopt command.
- Store the output of the
getopt command in the variable args. This is a string consisting of the command line arguments joined together with whitespace as the separator, and reordered to have options and their arguments, then --, then non-option operands.
- Split this string at whitespace, interpret each word as a wildcard pattern, and replace the pattern by the list of matching files if there are any.
- Use the resulting list as the positional arguments.
In Bourne/POSIX-style shells, $foo is the “split+glob” operator. You need double quotes to get the value of a variable: "$foo". But in zsh, $foo works differently: it expands to the value of foo except when foo is empty. So in zsh you get a single positional parameter.
The first use of the split+glob operator could be avoided by writing getopt optstring "$@": this would pass the positional parameters exactly to getopt. But when parsing the output of getopt, splitting is unavoidable (you could disable globbing though), because getopt uses spaces to separate arguments. The problem with getopt is that the output is ambiguous: there's no way to distinguish a space that was in an argument from a space that getopt added as a separator.
The right way to do this is with getopts. It's robust and portable.
while getopts optstring OPTLET; do
# We got the option -$OPTLET
case $OPTLET in
…
esac
done
shift $((OPTIND-1))
# Now the positional parameters are just the non-option operands
¹ At least the BSD version. The GNU version adds features that make it usable.
getoptsexample: does it matter if I callshiftin every case statement versus callingshiftall at once at the end? – shadowtalker Oct 04 '15 at 03:03shiftinside the loop,getoptswill get out of synch. You'd also have to updateOPTIND, which is howgetoptskeeps track of where it's at. – Gilles 'SO- stop being evil' Oct 04 '15 at 13:08