Handling of '--' in arguments of /bin/sh: POSIX vs implementations by Bash/Dash/FreeBSD's sh Handling of '--' in arguments of /bin/sh: POSIX vs implementations by Bash/Dash/FreeBSD's sh shell shell

Handling of '--' in arguments of /bin/sh: POSIX vs implementations by Bash/Dash/FreeBSD's sh


The answer to the question "What does Posix prescribe" is already present in the OP. But the important feature of the Posix standard is not highlighted: the -c option does not take an argument.

You can see this in the Synopsis:

sh -c [-abCefhimnuvx] [-o option]... [+abCefhimnuvx] [+o option]...      command_string [command_name [argument...]]

What the -c flag does is cause the positional parameters ("operands") to be interpreted in a different way. Without -c, they are interpreted as [command_file [argument...]]:

sh [-abCefhimnuvx] [-o option]... [+abCefhimnuvx] [+o option]...   [command_file [argument...]]

That, by the way, is why sh -c+x is an error. If -c took an argument, then it would be legal to include the argument in the same word.

So, to answer the more specific questions:

  1. Posix says "A single hyphen shall be treated as the first operand and then ignored...". Does that apply to a - immediately following -c?

    A: Yes, it does. -c is a complete option, and the - is therefore an operand. By contrast, - in -o - would not be treated as an operand. (It would be treated as an invalid option name.)

  2. Which one is right, Dash/Bash or FreeBSD?

    A: In this case, Dash and Bash are Posix-compliant, and FreeBSD's sh is not. FreeBSD's shell considerably predates the current Posix specification, and I don't believe it ever purported to be fully compliant to any Posix specification.

  3. How do I portably use sh to run a command whose name begins with a +?

    A: I would think the following would work on any shell:

    sh -c " +x"

    " +x" will not be recognized as an option because it doesn't start with a + or -, and sh -c causes the operand to be parsed as a shell command, so leading whitespace will be ignored. I don't have a copy of FreeBSD's ash to play with just now, so I welcome corrections.

    Or you could use a simple compound command:

    sh -c "{ +x; }"

    Possibly clearest (assuming the shell you're using implements the Posix-standard builtin command) is:

    sh -c "command +x"