我在 Python 文件的顶部看到了这些:
#!/usr/bin/env python
#!/usr/bin/env python3
在我看来,如果没有该行,文件的运行方式相同。
#!/usr/bin/env python
的网页。
如果您安装了多个版本的 Python,/usr/bin/env
将确保使用的解释器是您环境的 $PATH
上的第一个解释器。另一种方法是硬编码 #!/usr/bin/python
;没关系,但不太灵活。
在 Unix 中,用于解释的 executable 文件可以通过在第一行的开头使用 #!
来指示要使用的解释器,然后是解释器(以及它可能需要的任何标志) .
当然,如果您在谈论其他平台,则此规则不适用(但“shebang 行”没有害处,如果您将该脚本复制到具有 Unix 基础的平台(例如 Linux、Mac)会有所帮助, ETC)。
这称为 shebang line。作为 Wikipedia entry explains:
在计算中,shebang(也称为 hashbang、hashpling、pound bang 或 crunchbang)指的是字符“#!”当它们是解释器指令中的前两个字符时,作为文本文件的第一行。在类 Unix 操作系统中,程序加载器将这两个字符的存在视为文件是脚本的指示,并尝试使用文件中第一行其余部分指定的解释器执行该脚本。
另请参阅 Unix FAQ entry。
即使在 Windows 上,shebang 行不能确定要运行的解释器,您也可以通过在 shebang 行上指定选项来将选项传递给解释器。我发现在一次性脚本(例如我在回答 SO 问题时编写的脚本)中保留通用的 shebang 行很有用,因此我可以在 Windows 和 ArchLinux 上快速测试它们。
env utility 允许您在路径上调用命令:
剩下的第一个参数指定要调用的程序名称;根据PATH环境变量搜索。任何剩余的参数都作为参数传递给该程序。
.py
后缀作为文档关联到应用程序,Python 文件在 Explorer shell 中显示为可执行文件。如果该应用程序是特定于 Python 的 plauncher,那么您将获得 hash bang 处理。而已。
perl
并不关心有没有 /usr/bin/perl
,但会注意传递给它的选项。
扩展一下其他答案,这里有一个小例子,说明你的命令行脚本如何因不小心使用 /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")
which python
返回 /usr/bin/python
,则可以对本地目录路径进行硬编码:#!/usr/bin/python
。但这不如具有全局应用程序的 #!/usr/bin/env python
灵活。
为了运行python脚本,我们需要告诉shell三件事:
该文件是一个脚本 我们要执行脚本的解释器 所述解释器的路径
shebang #!
完成 (1.)。 shebang 以 #
开头,因为 #
字符是许多脚本语言中的注释标记。因此,解释器会自动忽略 shebang 行的内容。
env
命令完成 (2.) 和 (3.)。至quote "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 系统上。)
Linux 内核的 exec
系统调用本机理解 shebangs (#!
)
当你在 bash 上做:
./something
在 Linux 上,这会使用路径 ./something
调用 exec
系统调用。
在传递给 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
以查找 /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
,它也恰好是人类可读的)。让我们通过读取 /bin/ls
的前 4 个字节来确认,这是一个 ELF 可执行文件:
head -c 4 "$(which ls)" | hd
输出:
00000000 7f 45 4c 46 |.ELF|
00000004
因此,当内核看到这些字节时,它会获取 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很好地解决了这个问题。
env
的主要用例:pyenv
和其他版本管理器
您应该使用 #!/usr/bin/env python
而不仅仅是 /usr/bin/python
的一个主要用例是带有 pyenv
的版本管理器。
pyenv
允许您在单台机器上轻松安装多个 python 版本,以便能够更好地在没有虚拟化的情况下重现其他项目。
然后,它通过在 PATH 中设置其顺序来管理“当前”python 版本:例如,如 apt-get install for different python versions 所示,pyenv 管理的 python 可以位于:
/home/ciro/.pyenv/shims/python
因此与 /usr/bin/python
相去甚远,某些系统可能会通过 update-alternatives
symlinks 处理。
也许你的问题是在这个意义上:
如果您想使用:$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
/usr/bin/python
。在Linux下,系统安装的Python也几乎可以肯定是/usr/bin/python
(我没见过其他的,没有任何意义)。请注意,有些系统可能没有 /usr/bin/env
。
python
不是那么便携,它是分发默认的 Python 解释器。 Arch Linux 长期以来默认使用 Python 3,并且可能发行版也在考虑它,因为 Python 2 仅支持到 2020 年。
从技术上讲,在 Python 中,这只是一个注释行。
仅当您从 shell 运行 py 脚本 (从命令行)时才使用此行。这被称为 "Shebang!",它用于各种情况,而不仅仅是 Python 脚本。
在这里,它指示 shell 启动特定版本的 Python(以处理文件的其余部分。
py.exe
,它也可以在 Windows 上运行。这是标准 Python 安装的一部分。
强调大多数人错过的一件事可能是有意义的,这可能会妨碍立即理解。在终端中键入 python
时,通常不会提供完整路径。相反,在 PATH
环境变量中查找可执行文件。反过来,当您想直接执行 Python 程序 /path/to/app.py
时,必须告诉 shell 使用什么解释器(通过 hashbang,其他贡献者在上面解释了什么)。
Hashbang 需要解释器的完整路径。因此,要直接运行您的 Python 程序,您必须提供差异很大的 Python 二进制文件的完整路径,尤其是考虑到使用 virtualenv。为了解决可移植性,使用了 /usr/bin/env
的技巧。后者最初旨在就地改变环境并在其中运行命令。如果没有提供任何更改,它会在当前环境中运行命令,这实际上会导致相同的 PATH
查找,这可以解决问题。
Source from unix stackexchange
这是一个 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
它只是指定您要使用的解释器。要理解这一点,请通过执行 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 支持打印运算符。所以,现在您已经学会了如何在脚本解释器之间切换。
您可以使用 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 解释器版本。
$ python myscript.py
。
这意味着更多的历史信息而不是“真实”的答案。
请记住,在过去,你有很多类似 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
,则在处理 venv
时执行 which python
将显示 Python 解释器的路径:
~/Envs/venv/bin/python
请注意,虚拟环境的名称嵌入在 Python 解释器的路径中。因此,在脚本中硬编码此路径会导致两个问题:
如果您将脚本上传到存储库,则会强制其他用户使用相同的虚拟环境名称。这是如果他们首先发现问题。
即使您在其他虚拟环境中拥有所有必需的包,您也无法在多个虚拟环境中运行该脚本。
因此,为了补充 Jonathan 的答案,理想的 shebang 是 #!/usr/bin/env python
,不仅是为了跨操作系统的可移植性,还为了跨虚拟环境的可移植性!
#!/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
考虑到 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())
执行 python 文件时,可以使用 ./file.py
,其中 file 是文件名。 /usr/bin/env 是 PATH,然后 python 是 python 2 而 python3 是 python 3 (duh)
#!/usr/bin/env python
也可以让其他程序执行 python 文件,只要您使用 chmod +x file.py
。
这告诉脚本 python 目录在哪里!
#! /usr/bin/env python
不定期副业成功案例分享
chmod +x myscript.py
) 然后直接运行它来运行它:./myscript.py
,而不仅仅是python myscript.py
。env
提供了最大的灵活性,因为用户可以通过更改 PATH 来选择要使用的解释器。但是通常不需要这种灵活性,缺点是例如 linux 不能使用脚本名称作为ps
中的进程名称并恢复为“python”。例如,在为发行版打包 python 应用程序时,我建议不要使用env
。py
launcher 可以在 Windows 上使用 shebang 行。它包含在 Python 3.3 或 it can be installed independently 中。/usr/bin/env: Key has expired
而死亡。