如何以及谁决定将类似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中是如何工作的?
答案 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
脚本运行。