Answers:
的家当 #!
是一个的人类可读的实例幻数由字节串的0x23 0x21
,它被用来由exec()
家族的功能,以确定要执行的文件是否是一个脚本或二进制文件。当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上,在默认的外壳/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
exec()
此答案中提到的是系统调用,命令exec
是内置的Shell,这就是为什么您不能从Node.js或Java 调用exec
程序的原因。但是,例如Runtime.exec()
用Java 调用的任何shell命令最终都会由exec()
系统调用处理。
是的,它确实。顺便说一下,这不是一个愚蠢的问题。我的答案参考在这里。用#开始脚本!
这称为“ shebang”或“ bang”行。
它不过是Bash解释器的绝对路径。
它由数字符号和感叹号字符(#!)组成,后跟解释器的完整路径,例如/ bin / bash。
Linux下的所有脚本都使用第一行中指定的解释器执行。几乎所有bash脚本通常都以#!/ bin / bash开头(假设Bash已安装在/ bin中),这确保了即使使用Bash来解释脚本,即使如果在另一个shell下执行。Shebang是由Dennis Ritchie在7版Unix和8版之间在Bell Laboratories推出的。然后,它也被添加到伯克利的BSD生产线中。
忽略翻译行(shebang)
如果您未指定解释器行,则默认通常为/ bin / sh。但是,建议您设置#!/ bin / bash行。
#!/usr/bin/perl
#!/usr/local/bin/python
#!/usr/local/bin/ruby
用于支持多个系统的另一个常见的shebang条目是使用env定位要使用的解释器,例如#!/usr/bin/env perl
#!/usr/bin/env python
env
,实际上应该首选哪个?Python和Perl env
在shellscripts上经常使用,而通常会省略它,而shebang指向有问题的shell。
env
在$ PATH中找到程序有点麻烦。它没有设置环境变量,顾名思义。对于不同的用户,$ PATH可能是不同的结果。但是,它可以帮助脚本在无需修改的系统上运行,而这些系统将合理的perl解释器放在了奇怪的地方。
exec
Linux内核的系统调用#!
本身了解shebangs()
当您进行bash操作时:
./something
在Linux上,这会exec
使用path 调用系统调用./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
files添加自定义处理程序。该机制甚至通过文件扩展名支持处理程序。另一个应用程序是使用QEMU透明地运行不同体系结构的可执行文件。
我不认为POSIX指定shebang,但是:https ://unix.stackexchange.com/a/346214/32558,尽管它确实在基本原理部分以“如果系统支持可执行脚本,可能发生”。
./something
从shell 运行不会将完整路径传递到exec
,而是将输入的路径准确传递。您可以在答案中更正吗?不要echo "$0"
在你的脚本,你会看到这种情况。
实际上,如果您采取这种做法,则在shebang行中指出的可执行文件只是一个可执行文件。将某些文本解释器用作可执行文件是有意义的,但不是必须的。只是为了澄清和演示,我做了一个相当无用的测试:
#!/bin/cat
useless text
more useless text
still more useless text
将文件命名为test.txt并设置可执行位chmod u+x test.txt
,然后“调用”它:./test.txt
。如预期的那样,将输出文件的内容。在这种情况下,cat不会忽略shebang线。它仅输出所有行。因此,任何有用的口译员都应该能够忽略该提示行。对于bash,perl和PHP,它只是一条注释行。所以是的,这些忽略了shebang线。
根据我的收集,只要文件具有可执行位设置并被调用,内核就会分析文件头,以确定如何进行处理(据我所知,您可以通过LKM为自定义文件格式添加自定义处理程序)。如果文件似乎是带有#!的文本文件!组合开始时,将其执行分派到另一个可执行文件(通常是各种shell),该路径将在所述shebang之后的同一行中直接指定。然后内核继续执行外壳程序并传递文件以供处理。
简而言之,使用哪个shell调用脚本都没有关系-内核会将执行分派到相应的一种方式。
bash ./myscript.sh
和之间有明显的区别./myscript.sh
。
chmod +x my_shell_script.sh ; /path/to/my_shell_script.sh # or ./my_shell_script.sh if you happen to be in its directory