Since this happens in dash and dash is simpler, I looked there first.
Seems like exec.c is the place to look, and the relevant functionis are tryexec
, which is called from shellexec
which is called whenever the shell things a command needs to be executed. And (a simplified version of) the tryexec function is as follows:
STATIC void
tryexec(char *cmd, char **argv, char **envp)
{
char *const path_bshell = _PATH_BSHELL;
repeat:
execve(cmd, argv, envp);
if (cmd != path_bshell && errno == ENOEXEC) {
*argv-- = cmd;
*argv = cmd = path_bshell;
goto repeat;
}
}
So, it simply always replaces the command to execute with the path to itself (_PATH_BSHELL
defaults to "/bin/sh"
) if ENOEXEC
occurs. There’s really no magic here.
I find that FreeBSD exhibits identical behavior in bash
and in its own sh
.
The way bash
handles this is similar but much more complicated. If you want to look in to it further I recommend reading bash’s execute_command.c
and looking specifically at execute_shell_script
and then shell_execve
. The comments are quite descriptive.