在 Linux 和 BSD 中使用和不使用 shebang 的 Bash 脚本执行



当一个类似 Bash 的脚本作为没有 shebang 的二进制文件执行时,如何以及谁决定执行什么?

我猜使用 shebang 运行普通脚本是通过binfmt_script Linux 模块处理的,该模块检查 shebang,解析命令行并运行指定的脚本解释器。

但是,当有人在没有 shebang 的情况下运行脚本时会发生什么?我已经测试了直接execv方法,发现那里没有内核魔术 - 即这样的文件:

$ cat target-script
echo Hello
echo "bash: $BASH_VERSION"
echo "zsh: $ZSH_VERSION"

运行仅执行execv调用的已编译 C 程序会产生:

$ cat test-runner.cvoid main(( {        if (execv("./target-script", 0( == -1(                错误((;}$ ./测试运行程序./target-script:执行格式错误

但是,如果我从另一个 shell 脚本执行相同的操作,它会使用与原始脚本相同的 shell 解释器运行目标脚本:

$ cat test-runner.bash#!/bin/bash./target-script$ ./test-runner.bash你好bash:4.1.0(1(发布中尚:

如果我对其他 shell 做同样的技巧(例如,Debian 的默认sh - /bin/dash (,它也可以工作:

$ cat test-runner.dash  #!/bin/dash./target-script$ ./test-runner.dash你好砰:中尚:

神秘的是,它不能完全按照 zsh 的预期工作,并且不遵循一般方案。看起来 zsh 毕竟在此类文件上执行了/bin/sh

greycat@burrow-debian ~/z/test-runner $ cat test-runner.zsh#!/bin/zshecho ZSH_VERSION=$ZSH_VERSION./target-scriptgreycat@burrow-debian ~/z/test-runner $ ./test-runner.zshZSH_VERSION=4.3.10你好砰:中尚:

请注意,父脚本中的ZSH_VERSION有效,而子脚本中的ZSH_VERSION不起作用!

外壳(Bash,破折号(如何确定在没有shebang时执行的内容?我试图在 Bash/dash 源中挖掘那个地方,但是,唉,看起来我有点迷失在那里。谁能阐明确定没有 shebang 的目标文件应该作为脚本还是作为 Bash/dash 中的二进制文件执行的魔法?或者可能与内核/libc有某种交互,然后我欢迎解释它如何在Linux和FreeBSD内核/libcs中工作?

由于这发生在破折号中,而破折号更简单,所以我先看了看那里。

似乎exec.c是要查找的地方,并且相关的函数是tryexec的,它是从shellexec调用的,每当需要执行shell命令时都会调用。tryexec 函数的(简化版本(如下:

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;
        }
}

因此,如果发生ENOEXEC,它总是将要执行的命令替换为自身的路径(_PATH_BSHELL默认为 "/bin/sh"(。这里真的没有魔法。

我发现 FreeBSD 在 bash 和它自己的sh中表现出相同的行为。

bash处理此问题的方式类似,但要复杂得多。如果你想进一步研究它,我建议阅读 bash 的execute_command.c并专门查看 execute_shell_script 然后shell_execve .评论非常具有描述性。

(看起来 Sorpigal 已经涵盖了它,但我已经输入了这个,它可能很有趣。

根据 Unix FAQ 的第 3.16 节,shell 首先查看幻数(文件的前两个字节(。一些数字表示二进制可执行文件; #!表示该行的其余部分应解释为shebang。否则,shell 会尝试将其作为 shell 脚本运行。

此外,似乎csh查看第一个字节,如果它是#,它将尝试将其作为csh脚本运行。

最新更新