You're interpreting the man page wrong. Firstly, the part about --
signalling the end of options is irrelevant to what you're trying to do. The -c
overrides the rest of the command line from that point on, so that it's no longer going through bash's option handling at all, meaning that the --
would be passed through to the command, not handled by bash as an end of options marker.
The second mistake is that extra arguments are assigned as positional parameters to the shell process that's launched, not passed as arguments to the command. So, what you're trying to do could be done as one of:
/bin/bash -c 'echo "$0" "$1"' foo bar
/bin/bash -c 'echo "$@"' bash foo bar
In the first case, passing echo the parameters $0
and $1
explicitly, and in the second case, using "$@"
to expand as normal as "all positional parameters except $0". Note that in that case we have to pass something to be used as $0
as well; I've chosen "bash" since that's what $0
would normally be, but anything else would work.
As for the reason it's done this way, instead of just passing any arguments you give directly to the command you list: note that the documentation says "commands are read from string", plural. In other words, this scheme allows you to do:
/bin/bash -c 'mkdir "$1"; cd "$1"; touch "$2"' bash dir file
But, note that a better way to meet your original goal might be to use env
rather than bash
:
/usr/bin/env -- "ls" "-l"
If you don't need any of the features that a shell is providing, there's no reason to use it - using env
in this case will be faster, simpler, and less typing. And you don't have to think as hard to make sure it will safely handle filenames containing shell metacharacters or whitespace.
That gives you an opportunity to set/choose $0
when using an inline script. Otherwise, $0
would just be bash
.
Then you can do for instance:
$ echo foo > foo
$ bash -c 'wc -c < "${1?}"' getlength foo
4
$ rm -f bar
$ bash -c 'wc -c < "${1?}"' getlength bar
getlength: bar: No such file or directory
$ bash -c 'wc -c < "${1?}"' getlength
getlength: 1: parameter not set
Not all shells used to do that. The Bourne shell did. The Korn (and Almquist) shell chose to have the first parameter go to $1
instead. POSIX eventually went for the Bourne way, so ksh
and ash
derivatives reverted to that later (more on that at http://www.in-ulm.de/~mascheck/various/find/#shell). That meant that for a long time for sh
(which depending on the system was based on the Bourne, Almquist or Korn shell), you didn't know whether the first argument went into $0
or $1
, so for portability, you had to do things like:
sh -c 'echo foo in "$1"' foo foo
Or:
sh -c 'shift "$2"; echo txt files are "$@"' tentative-arg0 3 2 *.txt
Thankfully, POSIX has specified the new behavior where the first argument goes in $0
, so we can now portably do:
sh -c 'echo txt files are "$@"' meaningful-arg0-for-error *.txt
Best Answer
The difference between
--
and-
is that when-
is used, the-x
and-v
options are also unset.That's the usual way in which shells accepted the
-
, however, in POSIX, this option is "unspecified":The difference between
set --
and plainset
is quite commonly used.It is clearly explained in the manual:
The -- signals the "end of options" and any argument that follows even if it start with a - will be used as a Positional argument.
Instead:
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):