我在 Python 文件的顶部看到了这个:
- 对于 Python 2 文件
#!/usr/bin/env python
- 对于 Python 3 文件
#!/usr/bin/env python3
在我看来,如果没有该行,文件的运行方式相同。
如果您安装了多个版本的 Python,/usr/bin/env
将确保使用的解释器是您环境的$PATH
. 另一种方法是硬编码类似#!/usr/bin/python
; 没关系,但不太灵活。
在 Unix 中,要解释的可执行#!
文件可以通过在第一行的开头加上解释器(以及它可能需要的任何标志)来指示要使用的解释器。
当然,如果您在谈论其他平台,则此规则不适用(但“shebang 行”没有害处,如果您将该脚本复制到具有Unix 基础的平台(例如 Linux、Mac )会有所帮助, ETC)。
在计算中,shebang(也称为 hashbang、hashpling、pound bang 或 crunchbang)指的是字符“#!” 当它们是解释器指令中的前两个字符时,作为文本文件的第一行。在类 Unix 操作系统中,程序加载器将这两个字符的存在作为文件是脚本的指示,并尝试使用文件中第一行其余部分指定的解释器执行该脚本。
另请参阅Unix FAQ 条目。
即使在 Windows 上,shebang 行不能确定要运行的解释器,您也可以通过在 shebang 行上指定选项来将选项传递给解释器。我发现在一次性脚本中保留一个通用的 shebang 行(例如我在回答 SO 问题时编写的那些)很有用,因此我可以在 Windows 和ArchLinux上快速测试它们。
env 实用程序允许您在路径上调用命令:
剩下的第一个参数指定要调用的程序名称;根据
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 模块。
防止此类问题的一种方法是使用通常随大多数 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")
为了运行python脚本,我们需要告诉shell三件事:
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,但这种情况相当罕见,并且只发生在非 Linux 系统上。)
Linux内核的exec
系统调用原生理解shebangs( #!
)
当你在 bash 上做:
./something
在 Linux 上,这会exec
使用 path 调用系统调用./something
。
内核的这一行在传递给的文件上被调用exec
:https ://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
符号链接处理。
也许你的问题是在这个意义上:
如果你想使用:$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 软件/硬件系统 (OS/X) 下,它是:
#!/Applications/MacPython 2.5/python
或在 FreeBSD 上:
#!/usr/local/bin/python
然而,所有这些差异都可以通过使用以下方式使脚本在所有环境中可移植:
#!/usr/bin/env python
从技术上讲,在 Python 中,这只是一个注释行。
仅当您从 shell(从命令行)运行 py 脚本时才使用此行。这就是众所周知的“ Shebang ”!, 它用于各种情况,而不仅仅是 Python 脚本。
在这里,它指示 shell 启动特定版本的 Python(以处理文件的其余部分。
强调大多数人错过的一件事可能是有意义的,这可能会妨碍立即理解。当您输入python
终端时,您通常不会提供完整路径。PATH
相反,在环境变量中查找可执行文件。反过来,当你想直接执行 Python 程序时/path/to/app.py
,必须告诉 shell 使用什么解释器(通过hashbang,其他贡献者在上面解释的内容)。
Hashbang 需要解释器的完整路径。因此,要直接运行您的 Python 程序,您必须提供差异很大的 Python 二进制文件的完整路径,尤其是考虑到使用virtualenv时。为了解决可移植性,/usr/bin/env
使用了技巧。后者最初旨在就地改变环境并在其中运行命令。当没有提供任何更改时,它会在当前环境中运行命令,这有效地导致相同的PATH
查找,这可以解决问题。
这是一个 shell 约定,它告诉 shell 哪个程序可以执行脚本。
#!/usr/bin/env python
解析为 Python 二进制文件的路径。
这是推荐的方式,在文档中提出:
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
您可以使用 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
它只是指定您要使用的解释器。要理解这一点,请通过终端创建一个文件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 支持打印运算符。所以,现在您已经学会了如何在脚本解释器之间切换。
在我看来,如果没有该行,文件的运行方式相同。
如果是这样,那么也许您正在 Windows 上运行 Python 程序?Windows 不使用该行,而是使用文件扩展名来运行与文件扩展名关联的程序。
然而,在 2011 年,开发了一个“Python 启动器”,它(在某种程度上)模仿了 Windows 的这种 Linux 行为。这仅限于选择运行哪个 Python 解释器——例如,在安装了两者的系统上选择 Python 2 和 Python 3。启动器可以py.exe
通过 Python 安装选择安装,并且可以与.py
文件关联,以便启动器检查该行并依次启动指定的 Python 解释器版本。
这意味着更多的历史信息而不是“真实”的答案。
请记住,在过去,你有很多类似 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] 是的,有时人们仍然会这样做。不,我的腰带上没有戴萝卜或洋葱。
如果您在虚拟环境中运行脚本,例如venv
,那么which python
在工作时执行venv
将显示 Python 解释器的路径:
~/Envs/venv/bin/python
请注意,虚拟环境的名称嵌入在 Python 解释器的路径中。因此,在脚本中硬编码此路径会导致两个问题:
因此,为了补充Jonathan的答案,理想的 shebang#!/usr/bin/env python
不仅是为了跨操作系统的可移植性,还为了跨虚拟环境的可移植性!
#!/bin/bash/python3
or行#!/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
python2
考虑到和之间的可移植性问题python3
,您应该始终指定任一版本,除非您的程序与两者兼容。
一些发行版正在发布python
符号链接到python3
一段时间 - 不要依赖于python
存在python2
。
PEP 394强调了这一点:
为了容忍跨平台的差异,所有需要调用 Python 解释器的新代码都不应指定 python,而应指定 python2 或 python3(或更具体的 python2.x 和 python3.x 版本;请参阅迁移说明) . 在从 shell 脚本调用时、通过 system() 调用时或在任何其他上下文中调用时,应该在 shebangs 中进行这种区分。
当你有多个版本的 python 时,它告诉解释器使用哪个版本的 python 运行程序。
它允许您选择要使用的可执行文件;如果您可能安装了多个 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())
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
.
这告诉脚本 python 目录在哪里!
#! /usr/bin/env python