一尘不染

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

linux

当类似Bash的脚本以二进制形式执行而没有shebang时,如何确定由谁执行?

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

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

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

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

$ cat test-runner.c
void main(){
        如果(execv(“ ./ target-script”,0)== -1)
                perror();
}
$ ./测试运行器
./target-script:执行格式错误

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

$ cat test-runner.bash
#!/ bin / bash
./target-script

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

如果我对其他外壳(例如,Debian的默认设置sh- /bin/dash)执行相同的操作,那么它也可以工作:

$ cat test-runner.dash   
#!/ bin /破折号
./target-script

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

奇怪的是,它与zsh的配合使用效果不佳,也没有遵循一般的方案。看起来zsh /bin/sh毕竟对这样的文件执行了:

greycat @ burrow-debian〜/ z / test-runner $ cat test-runner.zsh 
#!/ bin / zsh
回声ZSH_VERSION = $ ZSH_VERSION
./target-script

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

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

如果没有shebang,shell(Bash,破折号)如何确定执行什么操作?我试图在Bash /
dash来源中挖掘该位置,但是,a,我似乎迷失了。任何人都可以阐明一种神奇的方法,这种方法确定应将没有shebang的目标文件作为脚本执行还是作为Bash
/ dash中的二进制文件执行?或者可能存在 某种互动的内核/ libc和那么我会在它是如何在Linux和FreeBSD内核/
libcs工作欢迎解释吗?


阅读 263

收藏
2020-06-02

共1个答案

一尘不染

由于这种情况发生在破折号中,并且破折号比较简单,因此我首先看了一下。

看起来像exec.c是查找的地方,而相关的函数是tryexecshellexec只要需要执行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;
        }
}

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

我发现FreeBSD bash本身就表现出相同的行为sh

的方式bash处理这个问题是类似的,但要复杂得多。如果您想进一步研究它,我建议您阅读bash的内容,然后execute_command.c专门阅读execute_shell_script然后shell_execve。这些评论具有描述性。

2020-06-02