在我看来,如果没有该行,文件运行相同。
如果您安装了多个版本的Python,/usr/bin/env
将确保使用的解释器是您环境中的第一个$PATH
。替代方案是硬编码像#!/usr/bin/python
;没关系,但不太灵活。
在Unix中,一个可以解释的可执行文件可以通过在第一行的开头有一个#!
,然后是解释器(以及它可能需要的任何标志)来指示要使用的解释器。
当然,如果您正在谈论其他平台,那么这条规则并不适用(但“shebang line”没有任何损害,如果您将该脚本复制到具有Unix基础的平台,例如Linux,Mac,将会有所帮助等)。
这是建议的方式,在文档中提出:
2.2.2。可执行的Python脚本
在BSD的Unix系统上,Python脚本可以直接执行,就像shell脚本一样,通过放置行
#! /usr/bin/env python3.2
来自http://docs.python.org/py3k/tutorial/interpreter.html#executable-python-scripts
这是一个shell约定,告诉shell哪个程序可以执行脚本。
#!/usr/bin/env python
解析为Python二进制文件的路径。
您可以使用virtualenv尝试此问题
这是test.py
#! /usr/bin/env python
import sys
print(sys.version)
创建虚拟环境
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 launcher"(在某种程度上)模仿Windows的这种Linux行为。这仅限于选择运行哪个Python解释器 - 例如在安装了两者的系统上选择Python 2和Python 3。启动器可选地通过Python安装安装为py.exe
,并且可以与.py
文件关联,以便启动器检查该行,然后启动指定的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'
因为python 3不支持打印操作符。
现在继续将代码的第一行更改为:
#!/usr/bin/env python2
它会工作,将test
打印到stdout,因为python2支持打印操作符。所以,现在你已经学会了如何在脚本解释器之间切换。
这意味着更多的历史信息而不是“真实的”答案。
请记住,当天你有很多像操作系统一样的unix,其设计师都有自己的东西放置的概念,有时根本不包括Python,Perl,Bash或许多其他GNU / Open Source的东西。
不同的Linux发行版甚至都是如此。在Linux上 - 前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站上它根本不会运行。
我想念我的sparc站。但不是很多。好的,现在你让我在E-Bay上乱逛。 Bastages。
[1]文件系统层次结构标准。 https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard
[2]是的,有时人们仍然会这样做。不,我的腰带上没有萝卜或洋葱。
如果您在虚拟环境中运行脚本,比如venv
,那么在处理which python
时执行venv
将显示Python解释器的路径:
~/Envs/venv/bin/python
请注意,虚拟环境的名称嵌入在Python解释器的路径中。因此,在脚本中对此路径进行硬编码将导致两个问题:
因此,为了增加Jonathan的答案,理想的shebang是#!/usr/bin/env python
,不仅仅是跨操作系统的可移植性,而且还是跨虚拟环境的可移植性!
考虑到python2
和python3
之间的可移植性问题,除非您的程序与两者兼容,否则应始终指定任一版本。
一些发行版现在将python
符号链接到python3
一段时间 - 不要依赖python
作为python2
。
PEP 394强调了这一点:
为了容忍跨平台的差异,需要调用Python解释器的所有新代码都不应该指定python,而是应该指定python2或python3(或更具体的python2.x和python3.x版本;请参阅Migration Notes)。当从shell脚本调用时,通过system()调用调用时,或者在任何其他上下文中调用时,应该在shebang中进行这种区分。
当你有多个版本的python时,它告诉解释器运行程序的python版本。
这告诉脚本python目录在哪里!
#! /usr/bin/env python
这被称为shebang line。作为Wikipedia entry explains:
在计算中,一个shebang(也称为hashbang,hashpling,pound bang或crunchbang)指的是字符“#!”当它们是解释器指令中的前两个字符作为文本文件的第一行时。在类Unix操作系统中,程序加载器将这两个字符作为文件是脚本的指示,并尝试使用文件中第一行其余部分指定的解释器来执行该脚本。
即使在Windows上,shebang行也没有确定要运行的解释器,您可以通过在shebang行上指定它们来将选项传递给解释器。我发现在一次性脚本(例如我在回答SO上的问题时写的那些)中保留通用的shebang系列很有用,所以我可以在Windows和ArchLinux上快速测试它们。
env utility允许您在路径上调用命令:
第一个剩下的参数指定要调用的程序名称;根据
PATH
环境变量搜索它。任何剩余的参数都作为参数传递给该程序。
在其他答案上稍微扩展一下,这里有一个小例子,说明你的命令行脚本如何通过谨慎使用/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")
为了运行python脚本,我们需要告诉shell三件事:
shebang #!
完成(1.)。 shebang以#
开头,因为#
字符是许多脚本语言中的注释标记。因此,解释器会自动忽略shebang线的内容。
env
命令完成(2.)和(3.)。引用“引力”
env
命令的一个常见用途是启动解释器,利用env将搜索$ PATH以获取它被命令启动的命令。由于shebang行需要指定绝对路径,并且由于各种解释器(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 myscript.py
你根本不需要那条线。系统将调用python,然后python interpreter将运行您的脚本。
但如果你打算使用:$./myscript.py
像普通程序或bash脚本一样直接调用它,你需要编写该行来指定系统使用哪个程序来运行它(并且还可以使用chmod 755
使其可执行)
从技术上讲,在Python中,这只是一个注释行。
仅当您从shell(从命令行)运行py脚本时才使用此行。这被称为"Shebang!",它用于各种情况,而不仅仅是Python脚本。
在这里,它指示shell启动特定版本的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
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内核将解析该行的其余部分,这将使用路径/usr/bin/env python
和当前文件作为第一个参数进行另一个exec调用:
/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
)。这将读取ELF文件,将其正确放入内存,然后使用它启动新进程。另见:How does kernel get an executable binary file running under linux?
最后,您可以使用binfmt_misc
机制添加自己的shebang处理程序。例如,您可以添加custom handler for .jar
files。此机制甚至通过文件扩展名支持处理程序。另一个应用是transparently run executables of a different architecture with 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
将是一种可能性,但是如果我们决定将命令重构为另一种语言,这就产生了一个主要问题。
Shebangs很好地解决了这个问题。
强调一个最错过的东西可能是有道理的,这可能会妨碍立即理解。在终端中键入python
时,通常不提供完整路径。相反,可执行文件在PATH
环境变量中查找。反过来,当你想直接执行Python程序时,/path/to/app.py
,必须告诉shell使用什么解释器(通过hashbang,其他贡献者在上面解释了什么)。
Hashbang希望有一个翻译的完整路径。因此,要直接运行Python程序,必须提供Python二进制文件的完整路径,这种路径会有很大差异,特别是考虑到使用virtualenv。为了解决可移植性,使用/usr/bin/env
的技巧。后者最初旨在就地改变环境并在其中运行命令。当没有提供任何改变时,它在当前环境中运行命令,这有效地导致相同的PATH
查找,这就是技巧。