1224

我在 Python 文件的顶部看到了这个:

  1. 对于 Python 2 文件
    #!/usr/bin/env python
    
  2. 对于 Python 3 文件
    #!/usr/bin/env python3
    

在我看来,如果没有该行,文件的运行方式相同。

4

22 回答 22

1204

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

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

当然,如果您在谈论其他平台,则此规则不适用(但“shebang 行”没有害处,如果您将该脚本复制到具有Unix 基础的平台(例如 Linux、Mac )会有所帮助, ETC)。

于 2010-03-11T23:52:41.493 回答
296

那就是所谓的shebang线。正如维基百科条目解释的那样:

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

另请参阅Unix FAQ 条目

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

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

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

于 2010-03-11T23:52:37.060 回答
164

/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 模块。

防止此类问题的一种方法是使用通常随大多数 Python 一起安装的版本化 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")
于 2010-03-12T00:35:46.513 回答
91

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

  1. 该文件是一个脚本
  2. 我们要执行脚本的解释器
  3. 所述解释器的路径

shebang#!完成 (1.)。shebang 以 a 开头,#因为该#字符是许多脚本语言中的注释标记。因此,解释器会自动忽略 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,但这种情况相当罕见,并且只发生在非 L​​inux 系统上。)

于 2012-03-26T00:07:27.443 回答
69

Linux内核的exec系统调用原生理解shebangs( #!)

当你在 bash 上做:

./something

在 Linux 上,这会exec使用 path 调用系统调用./something

内核的这一行在传递给的文件上被调用exechttps ://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例如 find /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)。让我们通过读取 的前 4 个字节来确认/bin/ls,这是一个 ELF 可执行文件:

head -c 4 "$(which ls)" | hd 

输出:

00000000  7f 45 4c 46                                       |.ELF|
00000004                                                                 

因此,当内核看到这些字节时,它会获取 ELF 文件,将其正确放入内存,并使用它启动一个新进程。另请参阅:内核如何获得在 linux 下运行的可执行二进制文件?

binfmt_misc最后,您可以使用该机制添加自己的 shebang 处理程序。例如,您可以为files添加自定义处理程序.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

是有可能的,但是如果我们决定将命令重构为另一种语言,那么它的主要问题就是一切都会中断。

Shebangs很好地解决了这个问题。

env:pyenv和其他版本管理器的主要用例

#!/usr/bin/env python为什么您应该使用而不是仅仅使用的一个主要用例/usr/bin/python是带有pyenv.

pyenv允许您在单台机器上轻松安装多个 python 版本,从而能够更好地在没有虚拟化的情况下重现其他项目。

然后,它通过在 PATH 中设置其顺序来管理“当前”python 版本:例如,如apt-get install 所示,对于不同的 python 版本,pyenv 管理的 python 可以位于:

/home/ciro/.pyenv/shims/python

如此接近/usr/bin/python,某些系统可能会通过update-alternatives符号链接处理。

于 2016-12-02T18:29:58.513 回答
51

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

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

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

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

像普通程序或 bash 脚本一样直接调用它,您需要编写该行以向系统指定用于运行它的程序,(并使其可执行chmod 755

于 2015-01-17T11:13:25.810 回答
42

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

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

#!/c/python3k/python 

在 GNU/Linux 发行版下,它是:

#!/usr/local/bin/python 

或者

#!/usr/bin/python

在最好的商业 Unix 软件/硬件系统 (OS/X) 下,它是:

#!/Applications/MacPython 2.5/python

或在 FreeBSD 上:

#!/usr/local/bin/python

然而,所有这些差异都可以通过使用以下方式使脚本在所有环境中可移植:

#!/usr/bin/env python
于 2011-07-20T22:34:10.727 回答
41

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

仅当您从 shell(从命令行)运行 py 脚本时才使用此行。这就是众所周知的Shebang ”!, 它用于各种情况,而不仅仅是 Python 脚本。

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

于 2010-03-11T23:58:43.873 回答
25

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

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

来自 unix stackexchange

于 2014-03-18T09:31:10.867 回答
18

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

#!/usr/bin/env python

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

于 2010-03-11T23:53:22.737 回答
14

这是推荐的方式,在文档中提出:

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

于 2011-12-11T09:34:30.403 回答
12

您可以使用 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
于 2012-12-21T14:52:58.933 回答
12

它只是指定您要使用的解释器。要理解这一点,请通过终端创建一个文件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 支持打印运算符。所以,现在您已经学会了如何在脚本解释器之间切换。

于 2016-11-26T19:13:56.113 回答
11

在我看来,如果没有该行,文件的运行方式相同。

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

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

于 2010-03-11T23:59:37.833 回答
9

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

请记住,在过去,你有很多类似 unix 的操作系统,它们的设计者都有自己的放置东西的概念,有时根本不包括 Python、Perl、Bash 或许多其他 GNU/开源的东西.

这甚至适用于不同的 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 站上,它可能根本无法运行。

我想念我的 sparc 站。但不是很多。好的,现在你让我在 E-Bay 上四处游荡。恶霸。

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

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

于 2017-11-28T21:40:17.087 回答
5

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

~/Envs/venv/bin/python

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

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

因此,为了补充Jonathan的答案,理想的 shebang#!/usr/bin/env python不仅是为了跨操作系统的可移植性,还为了跨虚拟环境的可移植性!

于 2016-04-05T22:05:42.707 回答
4

#!/bin/bash/python3or行#!/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
于 2020-06-08T07:41:12.877 回答
3

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

一些发行版正在发布python符号链接到python3一段时间 - 不要依赖于python存在python2

PEP 394强调了这一点:

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

于 2016-05-18T15:58:10.417 回答
3

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

于 2016-06-13T16:12:49.863 回答
2

它允许您选择要使用的可执行文件;如果您可能安装了多个 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())
于 2019-09-23T15:26:12.510 回答
0

When you execute the python file, you can use ./file.py where file is the name of the file. /usr/bin/env is the PATH, then python is python 2 and python3 is python 3 (duh)

#!/usr/bin/env python can also allow the python file to be executed by other programs, as long as you use chmod +x file.py.

于 2022-02-03T07:44:59.797 回答
-11

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

#! /usr/bin/env python
于 2015-01-17T11:22:45.450 回答