shebang python




为什么人们在Python脚本的第一行写入#!/ usr/bin/env python shebang? (14)

在我看来,这些文件在没有该行的情况下运行相同。

如果是这样,那么也许你在Windows上运行Python程序? Windows不使用该行 - 相反,它使用文件扩展名来运行与文件扩展名关联的程序。

然而在2011年,开发了一个“Python启动器” ,它在某种程度上模仿了Windows的这种Linux行为。 这仅限于选择运行哪个Python解释器 - 例如,在安装了两者的系统上选择Python 2和Python 3。 启动程序可以通过Python安装选择性安装为py.exe ,并且可以与.py文件关联,以便启动程序将检查该行并反过来启动指定的Python解释程序版本。

在我看来,这些文件在没有该行的情况下运行相同。


Linux内核的exec系统调用本地理解shebang( #!

当你在bash上做:

./something

在Linux上,这将调用具有完整路径的exec系统调用。

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

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

这读取文件的第一个字节,并将它们与#!进行比较 。

如果是这样的话,那么其余的行被Linux内核解析,这使得另一个exec调用路径/usr/bin/env python和当前文件作为第一个参数:

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

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

是的,你可以通过以下途径进行无限循环:

#!/a

和一个在/a的可执行文件

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

如果文件以不同的字节开始,那么exec系统调用将使用不同的处理程序。 另一个最重要的内置处理程序是用于ELF可执行文件的: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_elf.c#L1305https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_elf.c#L1305 ,它检查字节7f 45 4c 46 (它也是碰巧是人类可读的.ELF )。 这将读取ELF文件,将其正确放入内存中,并使用它启动新的进程。 另请参阅: 内核如何获得在Linux下运行的可执行二进制文件?

最后,您可以使用binfmt_misc机制添加您自己的shebang处理程序。 例如,您可以为.jar文件添加自定义处理程序: 运行JAR文件而不直接调用`java`此机制甚至通过文件扩展名支持处理程序。

但是,我不认为POSIX指定了shebangs: https://unix.stackexchange.com/a/346214/32558 ://unix.stackexchange.com/a/346214/32558,尽管它提到了基本原理部分,并且采用了“如果系统支持可执行脚本,发生”。


也许你的问题在这个意义上说:

如果你想使用: $python myscript.py

你根本不需要那条线。 系统会调用python,然后python解释器会运行你的脚本。

但是如果你打算使用: $./myscript.py

直接调用它就像一个普通的程序或bash脚本一样,你需要编写该行来指定系统使用哪个程序来运行它(也可以使用chmod 755来执行它)


从技术上讲,在Python中,这只是一条评论线。

只有从shell (从命令行)运行py脚本时才会使用此行。 这被称为"Shebang!" ,并且它在各种情况下使用,而不仅仅用于Python脚本。

在这里,它指示shell启动特定版本的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

Python 2.5中不存在json模块。

防止出现这种问题的一种方法是使用通常与大多数Pythons一起安装的版本化Python命令名称:

$ cat my_script.py 
#!/usr/bin/env python2.6
import json
print "hello, json"

如果您只需要区分Python 2.x和Python 3.x,则最新版本的Python 3也提供了python3名称:

$ cat my_script.py 
#!/usr/bin/env python3
import json
print("hello, json")

如果你在虚拟环境中运行你的脚本,比如venv ,那么在venvvenv时执行which python将显示Python解释器的路径:

~/Envs/venv/bin/python

请注意, 虚拟环境名称嵌入到Python解释器的路径中。 因此,在脚本中对此路径进行硬编码将导致两个问题:

  • 如果您将脚本上传到存储库,则会迫使其他用户拥有相同的虚拟环境名称 。 这是如果他们首先发现问题。
  • 即使您在其他虚拟环境中拥有所有必需的软件包,您也无法在多个虚拟环境中运行该脚本

因此,为了增加的回答,理想的shebang是#!/usr/bin/env python ,不仅适用于跨操作系统的可移植性,还适用于跨虚拟环境的可移植性!


它只是指定你想要使用的解释器。 要理解这一点,请通过执行touch test.py通过终端创建一个文件,然后在该文件中输入以下内容:

#!/usr/bin/env python3
print "test"

并执行chmod +x test.py以使您的脚本可执行。 在此之后,当你做./test.py你应该得到一个错误:

  File "./test.py", line 2
    print "test"
               ^
SyntaxError: Missing parentheses in call to 'print'

因为python3不支持打印操作符。

现在继续,将代码的第一行更改为:

#!/usr/bin/env python2

它会工作,打印test到标准输出,因为python2支持打印操作员。 所以,现在你已经学会了如何在脚本解释器之间切换。


它告诉解释器当你有多个版本的python时,运行该程序的Python版本。


更简洁,不完整但仍然有用的回应:虚拟环境兼容性。 Google virtualenv。 将其用于生产代码。


考虑到python2python3之间的可移植性问题,除非你的程序兼容两者,否则你应该总是指定任一版本。

一些发行版现在将python符号链接到python3一段时间 - 不要依靠python作为python2

PEP 394强调了这一点:

为了容忍跨平台的差异,需要调用Python解释器的所有新代码都不应该指定python,而应该指定python2或python3(或更具体的python2.x和python3.x版本;请参阅Migration Notes ) 。 这种区别应该在shebangs中进行,当从shell脚本调用时,通过system()调用调用时,或者在任何其他上下文中调用时。


这意味着更多的历史信息而不是“真实”的答案。

请记住,在那天你有许多unix像操作系统,其设计者都有自己的放置位置的概念,有时甚至不包括Python,Perl,Bash或许多其他GNU / Open Source的东西。

对于不同的Linux发行版甚至也是如此。 在Linux上 - pre-FHS [1] - 你可能在/ usr / bin /或/ usr / local / bin /中有python。 或者它可能没有安装,所以你建立了你自己的,并把它放在〜/ bin

Solaris是我工作过的最差的,部分是从Berkeley Unix到System V的过渡。你可以在/ usr /,/ usr / local /,/ usr / ucb,/ opt /等结束。对于一些非常长的路径。 我记得从Sunfreeware.com安装每个软件包到它自己的目录中的东西,但我不记得它是否将二进制文件符号链接到/ usr / bin中。

哦,有时/ usr / bin在NFS服务器上[2]。

所以env工具就是为了解决这个问题而开发的。

然后你可以编写#!/bin/env interpreter ,只要路径合适,事情就有合理的运行机会。 当然, 合理的意思是(对于Python和Perl)你也设置了合适的环境变量。 对于bash / ksh / zsh,它刚刚工作。

这很重要,因为人们传递shell脚本(比如perl和python),如果你在你的Red Hat Linux工作站上硬编码/ usr / bin / python,它会在SGI上坏掉......好吧,没有,我认为IRIX把python放在了正确的位置上。 但在Sparc车站,它可能无法运行。

我想念我的斯巴达站。 但不是很多。 好吧,现在你已经让我在电子海湾上徘徊了。 Bastages。

[1]文件系统层次结构标准。 https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard

[2]是的,有时候人们仍然会这样做。 不,我没有在我的腰带上戴萝卜或洋葱。


这是一个shell规范,它告诉shell哪个程序可以执行脚本。

#!/usr/bin/env python

解析为Python二进制文件的路径。


这样做的主要原因是使脚本跨操作系统环境移植。

例如在mingw下,python脚本使用:

#!/c/python3k/python 

在GNU / Linux发行版下它可以是:

#!/usr/local/bin/python 

要么

#!/usr/bin/python

并且在所有(OS / X)的最佳商用Unix sw / hw系统下,它是:

#!/Applications/MacPython 2.5/python

或在FreeBSD上:

#!/usr/local/bin/python

但是,所有这些差异都可以通过以下方式使脚本在所有人中间移植

#!/usr/bin/env python

这被称为shebang线 。 正如维基百科条目所解释的

在计算中,一个shebang(也称为hashbang,hashBound,pound bang或crunchbang)指的是字符“#!” 当它们是解释器指令中的前两个字符作为文本文件的第一行时。 在类Unix操作系统中,程序加载器将这两个字符作为文件是脚本的指示,并尝试使用文件第一行其余部分指定的解释器执行该脚本。

另请参阅Unix FAQ条目

即使在Windows中,shebang行不确定要运行的解释器,也可以通过在shebang行中指定它们将选项传递给解释器。 我发现在一次性脚本中保留一个通用的shebang行非常有用(比如我在回答问题时编写的脚本),所以我可以在Windows和ArchLinux上快速测试它们。

env实用程序允许您在路径上调用一个命令:

剩下的第一个参数指定要调用的程序名称; 它会根据PATH环境变量进行搜索。 任何剩余的参数都作为参数传递给该程序。







shebang