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

时间:2010-03-11 23:50:54

标签: python shell shebang

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

21 个答案:

答案 0 :(得分:992)

如果您安装了多个版本的Python,/usr/bin/env将确保使用的解释器是您环境$PATH上的第一个。另一种方法是对#!/usr/bin/python之类的东西进行硬编码;没关系,但不太灵活。

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

如果您正在谈论其他平台,当然,此规则不适用(但“shebang line”没有任何损害,如果您将该脚本复制到平台,将会有所帮助>一个Unix基础,如Linux,Mac等)。

答案 1 :(得分:244)

这称为shebang line。正如Wikipedia entry explains

  

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

另请参阅Unix FAQ entry

即使在Windows上,shebang行无法确定要运行的解释器,您也可以通过在shebang行上指定解释器来将选项传递给解释器。我觉得在一次性脚本中保留一个通用的shebang系列很有用(例如我在回答SO上的问题时写的那些),所以我可以在Windows和ArchLinux上快速测试它们。

env utility允许您在路径上调用命令:

  

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

答案 2 :(得分:145)

在其他答案上稍微扩展一下,这里有一个小例子,说明你的命令行脚本如何通过谨慎使用/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")

答案 3 :(得分:82)

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

  1. 该文件是一个脚本
  2. 我们想要执行脚本的解释器
  3. 所述翻译的路径
  4. shebang #!完成(1.)。 shebang以#开头,因为#字符是许多脚本语言中的注释标记。因此,解释器会自动忽略shebang行的内容。

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

      

    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系统上。)

答案 4 :(得分:40)

也许你的问题就是这个意义:

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

你根本不需要这条线。系统将调用python,然后python interpreter将运行你的脚本。

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

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

答案 5 :(得分:39)

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

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

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

答案 6 :(得分:35)

执行此操作的主要原因是使脚本可跨操作系统环境移植。

例如在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

答案 7 :(得分:32)

Linux内核的exec系统调用本身理解shebangs(#!

当您使用bash时:

./something

在Linux上,它使用路径exec调用./something系统调用。

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

  

if((bprm-&gt; buf [0]!=&#39;#&#39;)||(bprm-&gt; buf [1]!=&#39;!&#39;))

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

如果这是真的,那么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,虽然它确实提到了基本原理部分,并且形式为&#34;如果系统支持可执行脚本可能发生的事情&#34;。然而,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很好地解决了这个问题。

答案 8 :(得分:21)

强调最错过的一件事可能是有道理的,这可能会妨碍立即理解。当您在终端中键入python时,您通常不会提供完整路径。而是在PATH环境变量中查找可执行文件。反过来,当你想直接执行Python程序/path/to/app.py时,必须告诉shell使用什么解释器(通过 hashbang ,其他贡献者在上面解释的内容)。 / p>

Hashbang希望翻译完整路径。因此,要直接运行Python程序,必须提供Python二进制文件的完整路径,这种路径会有很大差异,特别是考虑到使用 virtualenv 。为了解决可移植性问题,使用了/usr/bin/env的技巧。后者最初旨在就地改变环境并在其中运行命令。如果没有提供更改,它会在当前环境中运行命令,这有效地导致了相同的PATH查找功能。

Source from unix stackexchange

答案 9 :(得分:12)

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

  

2.2.2。可执行的Python脚本

     

在BSD'ish Unix系统上,可以直接制作Python脚本   可执行文件,如shell脚本,通过放行

#! /usr/bin/env python3.2

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

答案 10 :(得分:10)

这是一个shell约定,告诉shell哪个程序可以执行脚本。

#!/usr/bin/env python

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

答案 11 :(得分:9)

您可以使用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

答案 12 :(得分:8)

  

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

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

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

答案 13 :(得分:6)

这意味着更多的历史信息而不是真实的&#34;回答。

请记住,当天你有很多像操作系统一样的unix,其设计师都有自己的东西放置的概念,有时候不包括Python,Perl,Bash或许多其他GNU /开源的东西 at all

对于不同的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]是的,有时人们仍然会这样做。不,我的腰带上没有萝卜或洋葱。

答案 14 :(得分:5)

如果您在虚拟环境中运行脚本,请说venv,然后在处理which python时执行venv将显示Python解释器的路径:

<强> ~/Envs/venv/bin/python

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

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

因此,要添加到Jonathan的答案,理想的shebang #!/usr/bin/env python ,不仅仅是跨操作系统的可移植性,而是跨虚拟环境的可移植性井!

答案 15 :(得分:5)

它只是指定您要使用的解释器。要理解这一点,请通过执行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打印到stdout,因为python2支持print运算符。所以,现在你已经学会了如何在脚本解释器之间切换。

答案 16 :(得分:3)

考虑到python2python3之间的可移植性问题,除非您的程序与这两者兼容,否则应始终指定任一版本。

某些发行版已将python符号链接发送到python3一段时间了 - 不要依赖于python python2

PEP 394强调了这一点:

  

为了容忍跨平台的差异,所有新代码都是如此   需要调用Python解释器不应该指定python,但是   而应该指定python2或python3(或更具体   python2.x和python3.x版本;见Migration Notes)。这个   当从壳调用时,应该在shebangs中进行区分   脚本,通过system()调用或任何调用时调用   其他背景。

答案 17 :(得分:3)

#!/bin/bash/python3行或#!/bin/bash/python行指定要使用的python编译器。您可能安装了多个python版本。例如,
a.py:

#!/bin/bash/python3
print("Hello World")

是python3脚本,并且
b.py:

#!/bin/bash/python
print "Hello World"

是python 2.x脚本
为了运行使用./a.py./b.py的文件,您需要事先赋予文件执行特权,否则执行将导致Permission denied错误。
要授予执行权限,

chmod +x a.py

答案 18 :(得分:2)

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

答案 19 :(得分:0)

它允许您选择要使用的可执行文件;这是非常 如果您有多个python安装和不同的模块,则非常方便 在每个,并希望选择。例如

#!/bin/sh
#
# Choose the python we need. Explanation:
# a) '''\' translates to \ in shell, and starts a python multi-line string
# b) "" strings are treated as string concat by python, shell ignores them
# c) "true" command ignores its arguments
# c) exit before the ending ''' so the shell reads no further
# d) reset set docstrings to ignore the multiline comment code
#
"true" '''\'
PREFERRED_PYTHON=/Library/Frameworks/Python.framework/Versions/2.7/bin/python
ALTERNATIVE_PYTHON=/Library/Frameworks/Python.framework/Versions/3.6/bin/python3
FALLBACK_PYTHON=python3

if [ -x $PREFERRED_PYTHON ]; then
    echo Using preferred python $ALTERNATIVE_PYTHON
    exec $PREFERRED_PYTHON "$0" "$@"
elif [ -x $ALTERNATIVE_PYTHON ]; then
    echo Using alternative python $ALTERNATIVE_PYTHON
    exec $ALTERNATIVE_PYTHON "$0" "$@"
else
    echo Using fallback python $FALLBACK_PYTHON
    exec python3 "$0" "$@"
fi
exit 127
'''

__doc__ = """What this file does"""
print(__doc__)
import platform
print(platform.python_version())

答案 20 :(得分:-8)

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

#! /usr/bin/env python