19

当类似 Bash 的脚本在没有 shebang 的情况下作为二进制文件执行时,如何以及由谁来确定执行什么?

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

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

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

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

$ cat test-runner.c
无效的主要(){
        if (execv("./target-script", 0) == -1)
                错误();
}
$ ./test-runner
./target-script: 执行格式错误

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

$ cat test-runner.bash
#!/bin/bash
./目标脚本

$ ./test-runner.bash
你好
bash:4.1.0(1)-发布
zsh:

如果我对其他 shell 执行相同的技巧(例如,Debian 的默认sh- /bin/dash),它也可以:

$ cat test-runner.dash   
#!/bin/破折号
./目标脚本

$ ./test-runner.dash
你好
重击:
zsh:

奇怪的是,它与 zsh 并没有像预期的那样工作,并且不遵循一般方案。看起来 zsh/bin/sh毕竟是在这些文件上执行的:

graycat@burrow-debian ~/z/test-runner $ cat test-runner.zsh
#!/bin/zsh
回声 ZSH_VERSION=$ZSH_VERSION
./目标脚本

graycat@burrow-debian ~/z/test-runner $ ./test-runner.zsh
ZSH_VERSION=4.3.10
你好
重击:
zsh:

请注意,ZSH_VERSION在父脚本中有效,而ZSH_VERSION在子脚本中没有!

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

4

2 回答 2

19

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

似乎 exec.c 是要查看的地方,并且相关的函数是tryexecshellexec每当需要执行命令的外壳程序时,都会调用它。并且(简化版)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;
        }
}

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

我发现 FreeBSDbash在它自己的sh.

处理这种情况的方式bash类似,但要复杂得多。如果您想进一步了解它,我建议您阅读 bash并execute_command.c专门查看. 评论很有​​描述性。execute_shell_scriptshell_execve

于 2011-09-01T10:27:42.627 回答
9

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

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

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

于 2011-09-01T10:37:25.373 回答