Ways to set positional parameters in bash

The difference between -- and - is that when - is used, the -x and -v options are also unset.

$ set -vx
$ echo "$-"
himvxBHs                # The options -v and -x are set.

$ set - a b c
$ echo "$-  <>  $@"     # The -x and -v options are turned off.
himBHs  <>  a b c

That's the usual way in which shells accepted the -, however, in POSIX, this option is "unspecified":

If the first argument is '-', the results are unspecified.

The difference between set -- and plain set is quite commonly used.
It is clearly explained in the manual:

-- If no arguments follow this option, then the positional parameters are unset. Otherwise, the positional parameters are set to the args, even if some of them begin with a -.

The -- signals the "end of options" and any argument that follows even if it start with a - will be used as a Positional argument.

$ set -- -a -b -e -f arg1
$ echo "$@"
-a -b -e -f arg1

Instead:

$ set -a -b -e -f arg1
$ echo "$@"
arg1

But also some shell options have changed.

Not using any of - or -- will allow the setting of set options with variables that expand to options names (even if quoted):

$ echo "$-"
himBHs

$ a='-f'
$ set "$a"

$ echo "$-"
fhimBHs

The difference between set argument and set -- argument is common to many other commands.

You sometimes have an argument that starts with a -, but you can't actually use it because the command thinks (because it starts with -) that it's actually a command option.

What the -- says is effectively: "Enough! everything that follows, even if it starts with -, is an actual argument".

Usually (according to manual pages) a lone - is equivalent to -- for this purpose.

Example

You might use:

set -- -a -b file1 file2  

to set $1, $2, $3 and $4 to -a, -b, file1 and file2 respectively. The -- isn't stored - it's just an indicator; without it, the -a and -b would be interpreted as possible option for the set command itself.

Tags:

Bash