我在一些脚本语言中注意到了这一点,但是在这个例子中,我使用的是python。在许多教程中,他们会以#!/usr/bin/python3。我不明白我们为什么要这么做。

操作系统不应该知道它是一个python脚本吗(显然它已经安装了,因为你引用了它) 如果用户使用的操作系统不是基于unix的呢 由于某种原因,该语言被安装在不同的文件夹中 用户版本不一致。特别是当它不是完整的版本号时(比如Python3 vs Python32)

如果有的话,我可以看到这破坏了python脚本,因为上面列出的原因。


当前回答

这条线表示如何。 它被忽略了。 它将无法运行,应该将其更改为指向正确的位置。或者应该使用env。 它将无法运行,并且可能无法在不同的版本下运行。

其他回答

这条线表示如何。 它被忽略了。 它将无法运行,应该将其更改为指向正确的位置。或者应该使用env。 它将无法运行,并且可能无法在不同的版本下运行。

这叫散列弹。如果从shell运行脚本,它将检查第一行,以确定应该启动哪个程序来解释脚本。

非基于Unix的操作系统将使用自己的规则来确定如何运行脚本。例如,Windows将使用文件名扩展名,而#将导致第一行被视为注释。

如果Python可执行文件的路径是错误的,那么脚本自然会失败。从标准约定指定的任何位置创建到实际可执行文件的链接都很容易。

实际上,决定一个文件是什么类型的文件是非常复杂的,所以现在操作系统不能只知道。它可以根据-进行很多猜测

扩展 泌尿道感染 MIME

但是命令行并没有为此烦恼,因为它运行在一个有限的向后兼容层上,从那时起,那些花哨的废话就没有任何意义了。如果你双击它,现代操作系统当然可以解决这个问题——但如果你从终端运行它,那么就不会,因为终端不会关心你花哨的操作系统特定的文件输入api。

关于其他几点。这很方便,运行起来也很方便

python3路径/ /你/脚本

如果你的python不在指定的路径中,那么它就不能工作,但我们倾向于安装一些东西来让这样的东西工作,而不是相反。是否使用*nix并不重要,它取决于shell是否考虑这一行,因为它是一个shellcode。例如,你可以在Windows下运行bash。

实际上你可以完全省略这一行,这只是意味着调用者必须指定一个解释器。此外,不要将解释器放在非标准位置,然后在不提供解释器的情况下尝试调用脚本。

#!/usr/bin/python3是shebang行。

shebang行定义解释器的位置。在本例中,python3解释器位于/usr/bin/python3shebang行也可以是bash、ruby、perl或任何其他脚本语言的解释器,例如:#!/bin/bash。

如果没有shebang行,操作系统不会知道它是一个python脚本,即使你在脚本上设置了执行标志(chmod +x script.py)并像./script.py那样运行它。要使脚本在python3中默认运行,可以将其作为python3 script.py调用,或者设置shebang行。

你可以使用#!/usr/bin/env python3用于不同系统之间的可移植性,以防语言解释器安装在不同的位置。

Linux内核的exec系统调用可以原生地理解shebangs (#!

当你在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内核将解析该行的其余部分,并以path /usr/bin/python3和current file作为第一个参数进行另一次exec调用:

/usr/bin/python3 /path/to/script.py

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

类似地,如果你决定使用env,你可能总是应该在python3位于不同位置的系统上工作,特别是pyenv,还可以参阅这个问题,shebang:

#!/usr/bin/env python3

最后类比地调用:

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

它完成了你对env python3的期望:在PATH中搜索python3并运行/usr/bin/python3 / 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)。让我们通过读取ELF可执行文件/bin/ls的前4个字节来确认:

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

输出:

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

因此,当内核看到这些字节时,它会获取ELF文件,将其正确地放入内存中,并使用它启动一个新进程。请参见:内核如何获得在linux下运行的可执行二进制文件?

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

但是我不认为POSIX指定了shebangs: https://unix.stackexchange.com/a/346214/32558,尽管它在基本原理部分提到了,并且以“如果系统支持可执行脚本,可能会发生一些事情”的形式出现。macOS和FreeBSD似乎也实现了它。

PATH搜索动机

shebangs存在的一个重要动机可能是,在Linux中,我们经常希望从PATH运行命令,就像这样:

basename-of-command

而不是:

/full/path/to/basename-of-command

但是,如果没有shebang机制,Linux如何知道如何启动每种类型的文件呢?

在命令中硬编码扩展:

 basename-of-command.py

或者在每个解释器上执行PATH搜索:

python3 basename-of-command

这是一种可能性,但这有一个主要的问题,如果我们决定将命令重构为另一种语言,那么一切都会崩溃。

她完美地解决了这个问题。

参见:为什么人们要写#!/usr/bin/env python脚本的第一行?