10

From https://www.gnu.org/software/bash/manual/bashref.html#The-Set-Builtin

set [--abefhkmnptuvxBCEHPT] [-o option-name] [argument …]
set [+abefhkmnptuvxBCEHPT] [+o option-name] [argument …]

...

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

- Signal the end of options, cause all remaining arguments to be assigned to the positional parameters. The -x and -v options are turned off. If there are no arguments, the positional parameters remain unchanged.

Using ‘+’ rather than ‘-’ causes these options to be turned off. The options can also be used upon invocation of the shell. The current set of options may be found in $-.

The remaining N arguments are positional parameters and are assigned, in order, to $1, $2, … $N. The special parameter # is set to N.

It seems that there are three ways to set the position parameters:

set -- argument
set - argument
set argument

What are their differences?

Thanks.

Tim
  • 98,580
  • 191
  • 570
  • 977
  • 2
    For what it's worth, [POSIX specifications](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#set) leave `set - ...` unspecified and only cover `set -- ...`. Beyond that, I don't know what you're asking. The difference is the number of hyphens. The difference in *effect* (if any) is **precisely described in the very document you've quoted.** Their differences metaphysically or in general semantics is outside the scope of this site. – Wildcard Jul 14 '17 at 22:19

2 Answers2

13

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
9

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.

Bob Eager
  • 3,520
  • 2
  • 14
  • 29