这可能是一个愚蠢的问题,但我仍然会问。如果我宣布了一个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
exec
Linux内核的系统调用原生理解shebangs ( #!
)
我没有注意到 OP 询问了有关 AIX 的问题,我将说出我对 Linux 的了解,并打赌其中存在很强的类比,或者至少满足大多数正在寻找 Linux 的 Google 员工:-)
当你在 bash 上执行以下操作时:
./something
在 Linux 上,这将使用exec
路径调用系统调用./something
。
内核的这一行在传递给的文件上被调用exec
:https://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。