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

时间:2011-09-01 09:27:50

标签: linux bash shell freebsd dash-shell

如何以及谁决定将类似Bash的脚本作为没有shebang的二进制文件执行时执行的操作?

我想用#{3}} Linux模块运行一个正常的脚本 shebang,它会检查一个shebang,parses命令行并运行指定的脚本解释器。

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

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

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

$ cat test-runner.c
void main() {
        if (execv("./target-script", 0) == -1)
                perror();
}
$ ./test-runner
./target-script: Exec format error

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

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

$ ./test-runner.bash 
Hello
bash: 4.1.0(1)-release
zsh: 

如果我对其他shell执行相同的操作(例如,Debian的默认sh - /bin/dash),它也有效:

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

$ ./test-runner.dash 
Hello
bash: 
zsh: 

神秘的是,它与zsh没有达到预期的效果,并且不遵循一般方案。看起来zsh毕竟在这些文件上执行了/bin/sh

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

greycat@burrow-debian ~/z/test-runner $ ./test-runner.zsh 
ZSH_VERSION=4.3.10
Hello
bash: 
zsh: 

请注意,父脚本中的ZSH_VERSION有效,而孩子中的ZSH_VERSION则没有!

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

2 个答案:

答案 0 :(得分:16)

由于这种情况发生在短划线上更简单,我先看了一下。

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

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

我发现FreeBSD在bash及其sh中表现出相同的行为。

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

答案 1 :(得分:9)

(看起来Sorpigal已经覆盖了它,但我已经输入了它,它可能会引起人们的兴趣。)

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

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