Search code examples
bashshellprocessshpid

does bash promise to optimize -c into plain exec in simple cases?


Consider the following invocation of bash

bash -c 'sleep 99'

Suppose I run that, and while waiting for it to finish sleeping, I run ps in another terminal. In that case, I do indeed find sleep 99 running. But its parent is NOT a process running bash -c. Instead, its parent is the process that INVOKED bash -c. I can think of only one explanation for this: bash noticed that since it's being asked to run a simple command, it permored the "optimized" sequence of exec sleep 99 instead of the "obvious" sequence of fork; exec sleep 99; wait; exit.

The reason I ask is that I noticed today that at work we have some Python scripts that very much depend on this behavior. (It wasn't obvious at first; we were using subprocess with shell=True... I didn't realize the implications until now.) I was trying to port them to windows and of course cmd.exe doesn't do this because exec is not traditionally a thing on Windows.

Then I wondered... is it actually documented anywhere that bash does this? I'd be quite surprised if it turns out that POSIX mandates this. I see no mention of it in the bash user manual. (Though it's a big manual.) Any idea to what extend this is guaranteed?


Solution

  • we have some Python scripts that [...] were using subprocess with shell=True

    Note that on UNIX-y systems, such as Linux or Mac, that will not launch Bash per se. Rather, it will launch /bin/sh. That might or might not be Bash (even on Linux), and if it is Bash then Bash runs in POSIX mode when launched by that name.

    is it actually documented anywhere that bash does this? [...] I see no mention of it in the bash user manual.

    I feel confident that my study of the manual was sufficient to support my affirmation that no, such behavior is not documented there. Nor is it in the manual page. I pretty much expect a mention in the source code, but that would be developer-facing, not user-facing.

    Any idea to what extend this is guaranteed?

    There being no user documentation of this behavior, the best answer is probably "not at all". Of course, Bash is software, so if you observe such behavior in one case, you should be able to rely on the same in sufficiently similar cases, but it's hard to characterize "sufficiently similar" very precisely.

    In the case of your Python scripts, if you are in fact relying on Bash to directly exec the specified command, then you should strongly consider not specifying shell=True. But do bear in mind that ensuring that there is no interposing shell is not the only effect of doing so. The usual reason for shell=True is in fact that you want to use shell features, such as I/O redirection, in the command.