shebang 是否确定运行脚本的 shell?

shebang 是否确定运行脚本的 shell?

这可能是一个愚蠢的问题,但我仍然会问。如果我宣布了一个shebang

#!/bin/bash 

在开头my_shell_script.sh,所以我总是必须使用 bash 调用这个脚本

[my@comp]$bash my_shell_script.sh

或者我可以使用例如

[my@comp]$sh my_shell_script.sh

我的脚本使用 shebang 确定正在运行的 shell? shell也有同样的情况吗ksh?我用的是AIX。

答案1

舍邦 #!是一个人类可读的实例幻数由字节 string 组成0x23 0x21,函数系列使用该字节exec()string 来确定要执行的文件是脚本还是二进制文件。当 shebang 存在时,exec()将运行 shebang 之后指定的可执行文件。

请注意,这意味着如果您通过在命令行上指定解释器来调用脚本,如中所示两个都问题中给出的情况,exec()将执行命令行上指定的解释器,它甚至不会查看脚本。

因此,正如其他人所指出的,如果您想exec()调用 shebang 行上指定的解释器,则脚本必须设置可执行位并作为 调用./my_shell_script.sh

使用以下脚本很容易演示该行为:

#!/bin/ksh
readlink /proc/$$/exe

解释:

  • #!/bin/ksh定义ksh为解释器。

  • $$保存当前进程的PID。

  • /proc/pid/exe是到进程可执行文件的符号链接(至少在 Linux 上;在 AIX 上,/proc/$$/object/a.out 是到可执行文件的链接)。

  • readlink将输出符号链接的值。

例子:

笔记:我在 Ubuntu 上演示了这一点,其中默认 shell/bin/sh是一个符号链接短跑即是 的符号链接,/bin/dash而又是 的符号链接。/bin/ksh/etc/alternatives/ksh/bin/pdksh

$ chmod +x getshell.sh
$ ./getshell.sh 
/bin/pdksh
$ bash getshell.sh 
/bin/bash
$ sh getshell.sh 
/bin/dash

答案2

是的,它确实。顺便说一句,这不是一个愚蠢的问题。我的回答的参考是这里。用 #! 开始脚本

  • 它被称为 shebang 或“bang”线。

  • 它只不过是 Bash 解释器的绝对路径。

  • 它由一个数字符号和一个感叹号字符 (#!) 组成,后跟解释器的完整路径,例如 /bin/bash。

    Linux 下的所有脚本都使用第一行指定的解释器执行几乎所有 bash 脚本通常都以 #!/bin/bash 开头(假设 Bash 已安装在 /bin 中)这确保了 Bash 将用于解释脚本,即使如果是在另一个shell下执行的话。 shebang 是由丹尼斯·里奇 (Dennis Ritchie) 在贝尔实验室的 Unix 版本 7 和版本 8 之间引入的。然后它也被添加到 Berkeley 的 BSD 系列中。

忽略口译员线路 (shebang)

如果不指定解释器行,默认值通常是 /bin/sh。但是,建议您设置 #!/bin/bash 行。

答案3

execLinux内核的系统调用原生理解shebangs ( #!)

我没有注意到 OP 询问了有关 AIX 的问题,我将说出我对 Linux 的了解,并打赌其中存在很强的类比,或者至少满足大多数正在寻找 Linux 的 Google 员工:-)

当你在 bash 上执行以下操作时:

./something

在 Linux 上,这将使用exec路径调用系统调用./something

内核的这一行在传递给的文件上被调用exechttps://github.com/torvalds/linux/blob/v4.8/fs/binfmt_script.c#L25

if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))

它读取文件的第一个字节,并将它们与#!.

如果比较为真,则该行的其余部分将由 Linux 内核解析,并exec使用路径/usr/bin/env python和当前文件作为第一个参数进行另一个调用:

/usr/bin/env python /path/to/script.py

这适用于任何用作#注释字符的脚本语言。

是的,您可以使用以下命令进行无限循环:

printf '#!/a\n' | sudo tee /a
sudo chmod +x /a
/a

Bash 识别出错误:

-bash: /a: /a: bad interpreter: Too many levels of symbolic links

#!只是恰好是人类可读的,但这不是必需的。

如果文件以不同的字节开始,则exec系统调用将使用不同的处理程序。另一个最重要的内置处理程序是针对 ELF 可执行文件的:https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_elf.c#L1305它检查字节7f 45 4c 46(这也恰好是人类可读的.ELF)。让我们通过读取 的前 4 个字节来确认这一点/bin/ls,它是一个 ELF 可执行文件:

head -c 4 "$(which ls)" | hd 

输出:

00000000  7f 45 4c 46                                       |.ELF|
00000004                                                                 

因此,当内核看到这些字节时,它会获取 ELF 文件,将其正确放入内存中,并用它启动一个新进程。也可以看看:https://stackoverflow.com/questions/8352535/how-does-kernel-get-an-executable-binary-file-running-under-linux/31394861#31394861

最后,您可以使用该binfmt_misc机制添加您自己的 shebang 处理程序。例如,您可以添加一个.jar文件的自定义处理程序。该机制甚至支持按文件扩展名进行处理。另一个应用是使用 QEMU 透明地运行不同架构的可执行文件

然而,我不认为 POSIX 指定了 shebangs:https://unix.stackexchange.com/a/346214/32558,尽管它确实在基本原理部分中提到了这一点,并且以“如果系统支持可执行脚本,则可能会发生某些事情”的形式提到了这一点。

答案4

根据我收集的信息,每当文件设置了可执行位并被调用时,内核都会分析文件头以确定如何继续(据我所知,您可以通过 LKM 添加自定义文件格式的自定义处理程序)。如果该文件看起来是带有 #! 的文本文件组合在开始时,它的执行被分派到另一个可执行文件(通常是某种 shell),其路径将在同一行中的所述 shebang 之后直接指定。然后内核继续执行 shell 并传递文件供其处理。

简而言之,使用哪个 shell 调用脚本并不重要 - 无论哪种方式,内核都会将执行分派给适当的 shell。

相关内容