Description
As detailed below and detailed further in the linked #1840 discussion thread, we are doing something like /bin/sh -c 'command "$@"' -- args...
when running commands with a shell, but --
does not have its usual meaning of causing arguments to be interpreted as non-options (or otherwise to be interpreted less specially).
Instead, the subsequent arguments are already passed through as the positional parameters, and the argument we are currently passing as --
becomes the special $0
parameter that appears when the shell produces messages. More informative alternatives are available. It is not obvious what approach is best, but I think we should use the name the shell uses to invoke itself, which will usually look something like:
/bin/sh: the body of the error message
Or, because some shells sometimes add further context information to their own messages, like:
/bin/sh: line 1: the body of the error message
This makes clear that the message comes from a shell, rather than the less informative approach of using --
as we currently are (or using _
, which may be more common than --
here but still not very informative). It distinguishes it from messages that come from an external command the shell runs, which is useful even when the operand to -c
is just a path, or quoted path, of an external command for the shell to run. This is because a shell may sometimes fail to run such a command.
When it is important, such error messages will usually show the name of the command, so the alternative of using the code operand to -c
or something derived from it risks duplicating information unnecessary.
Discussed in #1840
Originally posted by EliahKagan February 12, 2025
When use_shell
is true
and no optimization keeps a shell from actually being used, the impl From<Prepare> for Command
returns a std::process::Command
instance that, roughly speaking, will run something like /bin/sh -c 'command "$@"' -- args...
.
The argument after -c
and its operand is --
. This has been the case since functionality for passing arguments was added in d8e8b54 (#496).
Because the arguments before --
run a command with a shell, the arguments after --
are arbitrary arguments that are passed to the command or script rather than being treated specially by the shell, and --
is often used to prevent subsequent arguments from being treated as options or otherwise specially, it may look at first glance like --
does that here. Perhaps that was even its intent.
But this --
argument does not do that. Instead, to simplify from the standard, this way of invoking sh
is:
sh -c command_string [command_name [argument...]]
Where, as the standard documents for -c
:
Read commands from the command_string operand. Set the value of special parameter 0 (see 2.5.2 Special Parameters) from the value of the command_name operand and the positional parameters ($1, $2, and so on) in sequence from the remaining argument operands. No commands shall be read from the standard input.
This is to say that the effect of our passing --
here is to tell the script whose code is passed as the operand to -c
that it should consider its own name, i.e. the value of $0
, to be --
. One consequence of this is that errors produced in the shell itself are prefixed --:
. For example, in #1839, the error message is:
--: /Users/runner/work/gitoxide/gitoxide/target/debug/examples/arrow: No such file or directory
It seems to me that giving the script the name of --
may be confusing to people examining stderr of a process run with gix-command
, unless they are familiar with implementation details of gix-command
(as well as with the syntax of sh -c
).
If the goal of using --
rather than some other string was to prevent subsequent commands from being interpreted as shell options, then nothing special has to be done to avoid that, at least for POSIX-compatible shells. Arguments that come after the command string are never interpreted this way, instead always becoming positional parameters like $1
, $2
, $3
.
However, we do have to pass something where we are currently passing --
, if arguments meant to become $1
, $2
, $3
, ..., are passed. Otherwise the argument meant to be $1
(and thus the first word expanded from "$@"
) would become $0
, and the other positional parameters would, in effect, be shifted.
I'm not sure what would be better. In one-liners and causal use, I think _
is common. That is probably not better here. We should not use the value of the command
field (which becomes the command string, sometimes with "$@"
appended), or at least we should not use it unmodified. This is because the command
field is sometimes just a path, and having it be the value of $0
for the script would make it hard to distinguish messages produced by the shell from those produced by an external command the shell runs. The ability to distinguish this has already been useful in #1839.