在我看来,如果没有该行,文件运行相同 .
它只是指定您要使用的解释器 . 要理解这一点,请通过 touch test.py 在终端创建一个文件,然后在该文件中键入以下内容:
touch test.py
#!/usr/bin/env python3 print "test"
并执行 chmod +x test.py 以使您的脚本可执行 . 在此之后,当您执行 ./test.py 时,您应该收到错误消息:
chmod +x test.py
./test.py
File "./test.py", line 2 print "test" ^ SyntaxError: Missing parentheses in call to 'print'
因为python3不支持print运算符 .
现在继续将代码的第一行更改为:
#!/usr/bin/env python2
并且它会工作,将 test 打印到stdout,因为python2支持print运算符 . 所以,现在你已经学会了如何在脚本解释器之间切换 .
test
您可以使用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
The exec system call of the Linux kernel understands shebangs (#!) natively
当你在bash上做:
./something
在Linux上,它使用路径 ./something 调用 exec 系统调用 .
exec
在传递给 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
/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?
7f 45 4c 46
.ELF
最后,您可以使用 binfmt_misc 机制添加自己的shebang处理程序 . 例如,您可以添加custom handler for .jar files . 此机制甚至通过文件扩展名支持处理程序 . 另一个应用是transparently run executables of a different architecture with QEMU .
binfmt_misc
我不认为POSIX指定shebangs:https://unix.stackexchange.com/a/346214/32558,虽然它确实提到了基本原理部分,并且形式为"if executable scripts are supported by the system something may happen" .
强调一个最错过的东西可能是有道理的,这可能会妨碍立即理解 . 在终端中键入 python 时,通常不提供完整路径 . 相反,可执行文件在 PATH 环境变量中查找 . 反过来,当你想直接执行Python程序时,必须告诉shell使用什么解释器(通过hashbang,其他贡献者在上面解释了什么) .
python
PATH
Hashbang expects full path 给翻译 . 因此,要直接运行Python程序,您必须提供Python二进制文件的完整路径,这种路径会有很大差异,特别是考虑到使用virtualenv . 为了解决可移植性问题,使用 /usr/bin/env . 后者最初旨在就地改变环境并在其中运行命令 . 如果没有提供更改,它会在当前环境中运行命令,这有效地导致了相同的 PATH 查找功能 .
/usr/bin/env
Source from unix stackexchange
也许你的问题就是这个意义:
如果你想使用: $python myscript.py
$python myscript.py
你根本不需要那条线 . 系统将调用python,然后python interpreter将运行您的脚本 .
但如果你打算使用: $./myscript.py
$./myscript.py
像普通程序或bash脚本一样直接调用它,你需要编写该行来指定系统使用哪个程序来运行它(并且还可以使用 chmod 755 使其可执行)
chmod 755
当你有多个版本的python时,它告诉解释器运行程序的python版本 .
这告诉脚本python目录在哪里!
#! /usr/bin/env python
在其他答案上稍微扩展一下,这里有一个小例子,说明你的命令行脚本如何通过不谨慎地使用 /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 名称:
python3
$ cat my_script.py #!/usr/bin/env python3 import json print("hello, json")
这意味着更多的历史信息而不是“真正的”答案 .
请记住,当天你有很多像操作系统一样的unix,其设计师都有自己的东西放置的概念,有时根本不包括Python,Perl,Bash或许多其他GNU /开源的东西 .
不同的Linux发行版甚至都是如此 . 在Linux上 - 前FHS [1] - 你可能在/ usr / bin /或/ usr / local / bin /中有python . 或者它可能不是安装,所以你 Build 了自己的,并把它放在〜/ bin
Solaris是我工作过的最糟糕的,部分是从Berkeley Unix到System V的过渡 . 你可以在/ usr /,/ usr / local /,/ usr / ucb,/ opt /等中找到东西 . 这可能会使对于一些非常漫长的道路 . 我记得Sunfreeware.com安装每个软件包的东西's own directory, but I can' t回想一下它是否将二进制文件符号链接到/ usr / bin .
哦,有时/ usr / bin在NFS服务器上[2] .
所以开发了 env 实用程序来解决这个问题 .
env
然后你可以写 #!/bin/env interpreter 并且只要路径合适,事情就有合理的可能性 . 当然,合理的意思是(对于Python和Perl)你也设置了适当的环境变量 . 对于bash / ksh / zsh它只是起作用 .
#!/bin/env interpreter
这很重要,因为人们正在传递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]是的,有时人们仍然会这样做 . 不,我的腰带上没有萝卜或洋葱 .
这是建议的方式,在文档中提出:
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
从技术上讲,在Python中,这只是一个注释行 .
仅当您从shell(从命令行)运行py脚本时才使用此行 . 这被称为"Shebang!",它用于各种情况,而不仅仅是Python脚本 .
在这里,它指示shell启动特定版本的Python(以处理文件的其余部分) .
这是一个shell约定,告诉shell哪个程序可以执行脚本 .
#!/usr/bin/env python
解析为Python二进制文件的路径 .
如果是这样,那么也许你在Windows上运行Python程序? Windows不使用该行,而是使用文件扩展名来运行与文件扩展名关联的程序 .
However 在2011年,开发了一个"Python launcher"(在某种程度上)模仿Windows的这种Linux行为 . 这仅限于选择运行哪个Python解释器 - 例如在安装了两者的系统上选择Python 2和Python 3 . 启动器可选地通过Python安装安装为 py.exe ,并且可以与 .py 文件关联,以便启动器检查该行,然后启动指定的Python解释器版本 .
py.exe
.py
为了运行python脚本,我们需要告诉shell三件事:
该文件是脚本
我们想要执行脚本的解释器
所述翻译的路径
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系统上 . )
如果您在虚拟环境中运行脚本,例如 venv ,那么在处理 venv 时执行 which python 将显示Python解释器的路径:
venv
which python
~/Envs/venv/bin/python
请注意,Python解释器路径中的 name of the virtual environment is embedded . 因此,在脚本中对此路径进行硬编码将导致两个问题:
如果将脚本上载到存储库,则为 forcing other users to have the same virtual environment name . 这是他们首先确定问题 .
您 won't be able to run the script across multiple virtual environments 即使您在其他虚拟环境中拥有所有必需的软件包 .
因此,为了增加_75585的答案,理想的shebang是 #!/usr/bin/env python ,不仅仅是跨操作系统的可移植性,而且还是跨虚拟环境的可移植性!
这被称为shebang line . 作为Wikipedia entry explains:
在计算中,shebang(也称为hashbang,hashpling,pound bang或crunchbang)指的是字符“#!”当它们是解释器指令中的前两个字符作为文本文件的第一行时 . 在类Unix操作系统中,程序加载器将这两个字符作为文件是脚本的指示,并尝试使用文件中第一行其余部分指定的解释器来执行该脚本 .
另见Unix FAQ entry .
即使在Windows上,shebang行也没有确定要运行的解释器,您可以通过在shebang行上指定它们来将选项传递给解释器 . 我发现在一次性脚本中保留一个通用的shebang行很有用(例如我在回答问题时写的那些),所以我可以在Windows和_75513上快速测试它们 .
env utility允许您在路径上调用命令:
第一个剩余参数指定要调用的程序名称;根据PATH环境变量搜索它 . 任何剩余的参数都作为参数传递给该程序 .
这样做的主要原因是使脚本可以跨操作系统环境移植 .
例如,在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上:
但是,所有这些差异可以通过使用以下方式使脚本可移植到所有
如果您安装了多个版本的Python, /usr/bin/env 将确保使用的解释器是您环境 $PATH 上的第一个 . 替代方案是硬编码像 #!/usr/bin/python ;没关系,但不太灵活 .
$PATH
在Unix中,一个可以被解释的可执行文件可以通过在第一行的开头使用 #! ,然后是解释器(以及它可能需要的任何标志)来指示要使用的解释器 .
当然,如果您正在谈论其他平台,则此规则不适用(但是"shebang line"没有任何损害,如果您将该脚本复制到具有Unix基础的平台,例如Linux,Mac等,将会有所帮助) .
考虑到 python2 和 python3 之间的可移植性问题,除非程序与两者兼容,否则应始终指定任一版本 .
python2
一些发行版现在将 python 符号链接发送到 python3 一段时间 - 不要依赖 python 为 python2 .
PEP 394强调了这一点:
为了容忍跨平台的差异,所有需要调用Python解释器的新代码都不应该指定python,而是应该指定python2或python3(或更具体的python2.x和python3.x版本;请参阅迁移说明) . 当从shell脚本调用时,通过system()调用调用时,或者在任何其他上下文中调用时,应该在shebang中进行这种区分 .
19 回答
它只是指定您要使用的解释器 . 要理解这一点,请通过
touch test.py
在终端创建一个文件,然后在该文件中键入以下内容:并执行
chmod +x test.py
以使您的脚本可执行 . 在此之后,当您执行./test.py
时,您应该收到错误消息:因为python3不支持print运算符 .
现在继续将代码的第一行更改为:
并且它会工作,将
test
打印到stdout,因为python2支持print运算符 . 所以,现在你已经学会了如何在脚本解释器之间切换 .您可以使用virtualenv尝试此问题
这是test.py
创建虚拟环境
激活每个环境,然后检查差异
The exec system call of the Linux kernel understands shebangs (#!) natively
当你在bash上做:
在Linux上,它使用路径
./something
调用exec
系统调用 .在传递给
exec
的文件上调用内核的这一行:https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_script.c#L25这将读取文件的第一个字节,并将它们与
#!
进行比较 .如果这是真的,则Linux内核将解析该行的其余部分,这将使用路径
/usr/bin/env python
和当前文件作为第一个参数进行另一个exec调用:这适用于使用
#
作为注释字符的任何脚本语言 .是的,您可以通过以下方式进行无限循环:
Bash识别错误:
#!
恰好是人类可读的,但这不是必需的 .如果文件以不同的字节开头,则
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,虽然它确实提到了基本原理部分,并且形式为"if executable scripts are supported by the system something may happen" .
强调一个最错过的东西可能是有道理的,这可能会妨碍立即理解 . 在终端中键入
python
时,通常不提供完整路径 . 相反,可执行文件在PATH
环境变量中查找 . 反过来,当你想直接执行Python程序时,必须告诉shell使用什么解释器(通过hashbang,其他贡献者在上面解释了什么) .Hashbang expects full path 给翻译 . 因此,要直接运行Python程序,您必须提供Python二进制文件的完整路径,这种路径会有很大差异,特别是考虑到使用virtualenv . 为了解决可移植性问题,使用
/usr/bin/env
. 后者最初旨在就地改变环境并在其中运行命令 . 如果没有提供更改,它会在当前环境中运行命令,这有效地导致了相同的PATH
查找功能 .Source from unix stackexchange
也许你的问题就是这个意义:
如果你想使用:
$python myscript.py
你根本不需要那条线 . 系统将调用python,然后python interpreter将运行您的脚本 .
但如果你打算使用:
$./myscript.py
像普通程序或bash脚本一样直接调用它,你需要编写该行来指定系统使用哪个程序来运行它(并且还可以使用
chmod 755
使其可执行)当你有多个版本的python时,它告诉解释器运行程序的python版本 .
这告诉脚本python目录在哪里!
在其他答案上稍微扩展一下,这里有一个小例子,说明你的命令行脚本如何通过不谨慎地使用
/usr/bin/env
shebang行来解决问题:Python 2.5中不存在json模块 .
防止出现此类问题的一种方法是使用通常与大多数Pythons一起安装的版本化python命令名称:
如果您只需要区分Python 2.x和Python 3.x,Python 3的最新版本也提供
python3
名称:这意味着更多的历史信息而不是“真正的”答案 .
请记住,当天你有很多像操作系统一样的unix,其设计师都有自己的东西放置的概念,有时根本不包括Python,Perl,Bash或许多其他GNU /开源的东西 .
不同的Linux发行版甚至都是如此 . 在Linux上 - 前FHS [1] - 你可能在/ usr / bin /或/ usr / local / bin /中有python . 或者它可能不是安装,所以你 Build 了自己的,并把它放在〜/ bin
Solaris是我工作过的最糟糕的,部分是从Berkeley Unix到System V的过渡 . 你可以在/ usr /,/ usr / local /,/ usr / ucb,/ opt /等中找到东西 . 这可能会使对于一些非常漫长的道路 . 我记得Sunfreeware.com安装每个软件包的东西's own directory, but I can' t回想一下它是否将二进制文件符号链接到/ 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]是的,有时人们仍然会这样做 . 不,我的腰带上没有萝卜或洋葱 .
这是建议的方式,在文档中提出:
来自http://docs.python.org/py3k/tutorial/interpreter.html#executable-python-scripts
从技术上讲,在Python中,这只是一个注释行 .
仅当您从shell(从命令行)运行py脚本时才使用此行 . 这被称为"Shebang!",它用于各种情况,而不仅仅是Python脚本 .
在这里,它指示shell启动特定版本的Python(以处理文件的其余部分) .
这是一个shell约定,告诉shell哪个程序可以执行脚本 .
解析为Python二进制文件的路径 .
如果是这样,那么也许你在Windows上运行Python程序? Windows不使用该行,而是使用文件扩展名来运行与文件扩展名关联的程序 .
However 在2011年,开发了一个"Python launcher"(在某种程度上)模仿Windows的这种Linux行为 . 这仅限于选择运行哪个Python解释器 - 例如在安装了两者的系统上选择Python 2和Python 3 . 启动器可选地通过Python安装安装为
py.exe
,并且可以与.py
文件关联,以便启动器检查该行,然后启动指定的Python解释器版本 .为了运行python脚本,我们需要告诉shell三件事:
该文件是脚本
我们想要执行脚本的解释器
所述翻译的路径
shebang
#!
完成(1.) . shebang以#
开头,因为#
字符是许多脚本语言中的注释标记 . 因此,解释器会自动忽略shebang行的内容 .env
命令完成(2.)和(3.) . 引用"grawity,"如果您在虚拟环境中运行脚本,例如
venv
,那么在处理venv
时执行which python
将显示Python解释器的路径:~/Envs/venv/bin/python
请注意,Python解释器路径中的 name of the virtual environment is embedded . 因此,在脚本中对此路径进行硬编码将导致两个问题:
如果将脚本上载到存储库,则为 forcing other users to have the same virtual environment name . 这是他们首先确定问题 .
您 won't be able to run the script across multiple virtual environments 即使您在其他虚拟环境中拥有所有必需的软件包 .
因此,为了增加_75585的答案,理想的shebang是 #!/usr/bin/env python ,不仅仅是跨操作系统的可移植性,而且还是跨虚拟环境的可移植性!
这被称为shebang line . 作为Wikipedia entry explains:
另见Unix FAQ entry .
即使在Windows上,shebang行也没有确定要运行的解释器,您可以通过在shebang行上指定它们来将选项传递给解释器 . 我发现在一次性脚本中保留一个通用的shebang行很有用(例如我在回答问题时写的那些),所以我可以在Windows和_75513上快速测试它们 .
env utility允许您在路径上调用命令:
这样做的主要原因是使脚本可以跨操作系统环境移植 .
例如,在mingw下,python脚本使用:
在GNU / Linux发行版下,它是:
要么
在所有(OS / X)的最佳商业Unix sw / hw系统下,它是:
或者在FreeBSD上:
但是,所有这些差异可以通过使用以下方式使脚本可移植到所有
如果您安装了多个版本的Python,
/usr/bin/env
将确保使用的解释器是您环境$PATH
上的第一个 . 替代方案是硬编码像#!/usr/bin/python
;没关系,但不太灵活 .在Unix中,一个可以被解释的可执行文件可以通过在第一行的开头使用
#!
,然后是解释器(以及它可能需要的任何标志)来指示要使用的解释器 .当然,如果您正在谈论其他平台,则此规则不适用(但是"shebang line"没有任何损害,如果您将该脚本复制到具有Unix基础的平台,例如Linux,Mac等,将会有所帮助) .
考虑到
python2
和python3
之间的可移植性问题,除非程序与两者兼容,否则应始终指定任一版本 .一些发行版现在将
python
符号链接发送到python3
一段时间 - 不要依赖python
为python2
.PEP 394强调了这一点: