为什么人们在Python脚本的第一行编写#!/ usr / bin / env python?

在我看来,这些文件运行相同,没有这一行。

如果您安装了多个版本的Python,则/usr/bin/env将确保使用的解释器是您环境的$PATH的第一个。 替代方法是硬编码像#!/usr/bin/python ; 没关系,但不太灵活。

在Unix中,一个可解释的可执行文件可以通过#!来指示要使用的解释器#! 在第一行的开始,然后是解释器(以及它可能需要的任何标志)。

如果你在谈论其他平台,当然,这个规则并不适用(但是“shebang line”并没有什么坏处,如果你把这个脚本复制到一个Unix基础的平台,比如Linux,Mac等等)。

这就是所谓的三帮线 。 正如维基百科条目所解释的 :

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

请参阅Unix FAQ条目 。

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

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

剩下的第一个参数指定要调用的程序名称; 它是根据PATH环境variables进行search的。 任何剩余的参数都作为parameter passing给该程序。

在其他答案上稍微扩展一下,下面是一些小小的例子,说明你的命令行脚本如何不小心使用/usr/bin/env

 $ /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") 

为了运行python脚本,我们需要告诉shell三件事情:

  1. 该文件是一个脚本
  2. 我们要执行脚本的解释器
  3. 说解释者的path

shebang #! 完成(1.)。 shebang以#开头,因为#字符是许多脚本语言中的注释标记。 因此,shebang行的内容会被解释器自动忽略。

env命令完成(2.)和(3.)。 引用“grawity”

env命令的一个常见用途是启动解释器,通过使用env将search$ PATH的命令来启动它。 由于shebang行需要指定绝对path,并且由于各种解释器(perl,bash,python)的位置可能有很大差异,所以通常使用:

#!/usr/bin/env perl而不是猜测它是/ bin / 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系统上)。

从技术上讲,在Python中,这只是一条注释行。

只有从shell (从命令行)运行py脚本才能使用此行。 这被称为“ Shebang !” ,它在各种情况下使用,而不仅仅是Python脚本。

在这里,它指示shell启动特定版本的Python(以处理文件的其余部分。

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

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

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

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

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

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

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

 #!/c/python3k/python 

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

 #!/usr/local/bin/python 

要么

 #!/usr/bin/python 

和所有的最好的商业Unix的SW / HW系统(OS / X)下,它是:

 #!/Applications/MacPython 2.5/python 

或在FreeBSD上:

 #!/usr/local/bin/python 

但是,所有这些差异都可以通过使用以下脚本来使脚本可移植:

 #!/usr/bin/env python 

强调大部分错过的东西可能是有意义的,这可能会阻止立即理解。 在terminal中键入python ,通常不会提供完整的path。 而是在PATH环境variables中查找可执行文件。 反过来,当你想直接执行一个Python程序/path/to/app.py ,必须告诉shell使用什么解释器(通过hashbang ,其他贡献者正在上面解释)。

Hashbang期待完全的解释。 因此要直接运行你的Python程序,你必须提供Python二进制文件的完整path,这个path显着不同,特别是考虑到了virtualenv的使用。 为了解决可移植性,使用/usr/bin/env的技巧。 后者原本是为了在原地改变环境,并在其中运行一个命令。 当没有提供修改的时候,它会在当前环境中运行这个命令,这有效地导致了相同的PATH查找。

来自unix stackexchange

这是build议的方式,在文档中提出:

2.2.2。 可执行的Python脚本

在BSD的Unix系统上,Python脚本可以直接执行,比如shell脚本

 #! /usr/bin/env python3.2 

来自http://docs.python.org/py3k/tutorial/interpreter.html#executable-python-scripts

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

当你在bash上做:

 ./something 

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

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

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

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

如果是这样的话,那么剩下的内核就会被Linux内核parsing,这个内核会再次调用path/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#L1305它检查字节7f 45 4c 46 (也是碰巧是人类可读的.ELF )。 这将读取ELF文件,并将其正确放入内存中,并使用该文件启动一个新的进程。 另请参阅: 内核如何获得在Linux下运行的可执行二进制文件?

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

我不认为POSIX指定shebangs: https ://unix.stackexchange.com/a/346214/32558

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

  #!/ usr / bin / env python 

parsing为Python二进制文件的path。

你可以用virtualenv来试试这个问题

这里是test.py

 #! /usr/bin/env python import sys print(sys.version) 

创build虚拟环境

 virtualenv test2.6 -p /usr/bin/python2.6 virtualenv test2.7 -p /usr/bin/python2.7 

激活每个环境,然后检查差异

 echo $PATH ./test.py 

在我看来,这些文件运行相同,没有这一行。

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

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

如果你正在虚拟环境中运行你的脚本,比如说venv ,那么在venvvenv which python时候会显示Python解释器的path:

~/Envs/venv/bin/python

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

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

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

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

有些发行版现在已经将python符号链接到python3了 – 不要依赖python作为python2

PEP 394强调了这一点:

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

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

 #!/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版本。

这告诉脚本在哪里是python目录!

 #! /usr/bin/env python