我在Python文件的顶部看到了这些:
#!/usr/bin/env python
#!/usr/bin/env python3
在我看来,没有这一行,文件运行是一样的。
我在Python文件的顶部看到了这些:
#!/usr/bin/env python
#!/usr/bin/env python3
在我看来,没有这一行,文件运行是一样的。
当前回答
为了运行python脚本,我们需要告诉shell三件事:
该文件是一个脚本 我们希望哪个解释器执行脚本 所述解释器的路径
shebang #!完成(1)。shebang以#开头,因为#字符在许多脚本语言中是注释标记。因此,解释器会自动忽略shebang行的内容。
env命令完成(2.)和(3.)。引用“引力”,
env命令的一个常见用法是通过使 使用事实,env将搜索$PATH为它被告知的命令 推出。由于shebang线需要一个绝对路径 指定的,并且由于各种解释器(perl、bash、 Python)可能会有很大的变化,通常使用: #!/usr/bin/env perl,而不是试图猜测是否是 /usr/bin/perl, /usr/local/bin/perl, /usr/local/pkg/perl, /fileserver/usr/bin/perl,或者用户的/home/ mrdaniel /usr/bin/perl 系统…… 另一方面,env几乎总是在/usr/bin/env.中(除了 当它不是的时候;有些系统可能会使用/bin/env,但那是一个 相当罕见的情况,只发生在非linux系统上。)
其他回答
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内核解析,它将使用以下命令执行另一个exec调用:
可执行:/usr/bin/env 第一个参数:python 第二个参数:脚本路径
因此等价于:
/usr/bin/env python /path/to/script.py
env是一个可执行文件,搜索PATH,例如找到/usr/bin/python,然后最后调用:
/usr/bin/python /path/to/script.py
Python解释器会看到#!但是在Python中#是注释字符,所以这一行作为常规注释被忽略了。
是的,你可以用:
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搜索:
python basename-of-command
这是一种可能性,但这有一个主要的问题,如果我们决定将命令重构为另一种语言,那么一切都会崩溃。
她完美地解决了这个问题。
env: pyenv和其他版本管理器的主要用例
为什么应该使用#!/usr/bin/env python而不是/usr/bin/python是pyenv的版本管理器。
Pyenv允许您轻松地在一台机器上安装多个python版本,以便能够在没有虚拟化的情况下更好地再现其他项目。
然后,它通过在PATH中设置其顺序来管理“当前”python版本:例如,对于不同的python版本,如apt-get install所示,pyenv托管的python可以位于:
/home/ciro/.pyenv/shims/python
所以与/usr/bin/python完全不同,有些系统可能会通过更新替代符号链接来处理。
如果你在虚拟环境(比如venv)中运行脚本,那么在venv上执行哪个python将显示python解释器的路径:
~ / env / venv / bin / python
请注意,虚拟环境的名称嵌入在Python解释器的路径中。因此,在脚本中硬编码这个路径会导致两个问题:
如果将脚本上传到存储库,则会强制其他用户使用相同的虚拟环境名称。前提是他们首先发现了问题。 即使在其他虚拟环境中有所有必需的包,也不能跨多个虚拟环境运行脚本。
因此,为乔纳森的回答补充一点,理想的shebang是#!/usr/bin/env python,不仅可以跨操作系统的可移植性,还可以跨虚拟环境的可移植性!
扩展一下其他的答案,这里有一个小例子,说明你的命令行脚本会因为不小心使用/usr/bin/env shebang行而陷入麻烦:
$ /usr/local/bin/python -V
Python 2.6.4
$ /usr/bin/python -V
Python 2.5.1
$ cat my_script.py
#!/usr/bin/env python
import json
print "hello, json"
$ PATH=/usr/local/bin:/usr/bin
$ ./my_script.py
hello, json
$ PATH=/usr/bin:/usr/local/bin
$ ./my_script.py
Traceback (most recent call last):
File "./my_script.py", line 2, in <module>
import json
ImportError: No module named json
json模块在Python 2.5中不存在。
防止这类问题的一种方法是使用通常与大多数python一起安装的版本化python命令名:
$ cat my_script.py
#!/usr/bin/env python2.6
import json
print "hello, json"
如果你只需要区分Python 2。3. Python。x,最近发布的Python 3也提供了一个python3名称:
$ cat my_script.py
#!/usr/bin/env python3
import json
print("hello, json")
也许你的问题是这样的:
如果你想使用:$python myscript.py
你根本不需要那句台词。系统将调用python,然后python解释器将运行您的脚本。
但如果你打算使用:$./myscript.py
像普通程序或bash脚本一样直接调用它,您需要编写这一行来指定系统使用哪个程序来运行它(并使其可执行chmod 755)
它告诉解释器,当你有多个版本的python时,使用哪个版本的python运行程序。