我注意到我从其他人那里获得的一些脚本具有 shebang ,#!/path/to/NAME
而其他脚本(使用相同的工具,NAME)具有 shebang #!/usr/bin/env NAME
。
两者似乎都工作正常。在教程(例如,Python 教程)中,似乎有人建议后者更好。但是,我不太明白为什么会这样。
我意识到,为了使用后一个 shebang,NAME 必须位于 PATH 中,而第一个 shebang 没有此限制。
另外,(对我来说)似乎第一个是更好的 shebang,因为它精确地指定了 NAME 的位置。因此,在这种情况下,如果 NAME 有多个版本(例如,/usr/bin/NAME、/usr/local/bin/NAME),则第一种情况指定使用哪个版本。
我的问题是为什么第一个 shebang 优于第二个?
答案1
这并不一定更好。
的优点#!/usr/bin/env python
是它将使用python
用户的$PATH
.
这坏处的#!/usr/bin/env python
原因是它将使用python
用户的$PATH
.
这意味着脚本的行为可能会有所不同,具体取决于运行它的人。对于一个用户,它可能会使用/usr/bin/python
随操作系统一起安装的。另一方面,它可能使用一个/home/phred/bin/python
不太正常工作的实验。
如果python
仅安装在 中/usr/local/bin
,则没有/usr/local/bin
in 的用户$PATH
甚至无法运行该脚本。 (这在现代系统上可能不太可能,但对于更晦涩的解释器来说很容易发生。)
通过指定,#!/usr/bin/python
您可以准确指定将使用哪个解释器来运行脚本在特定系统上。
另一个潜在的问题是这个#!/usr/bin/env
技巧不允许您将参数传递给解释器(除了隐式传递的脚本名称)。这通常不是问题,但也可能是问题。许多 Perl 脚本都是用 编写的#!/usr/bin/perl -w
,但use warnings;
现在推荐使用它来替代。 Csh 脚本应该使用#!/bin/csh -f
-- 但 csh 脚本是不建议首先。但可能还有其他例子。
当我在新系统上设置帐户时,我在个人源代码控制系统中安装了许多 Perl 脚本。我使用一个安装程序脚本,该脚本#!
在将每个脚本安装到我的$HOME/bin
. (除了最近,我没有必要使用任何其他东西#!/usr/bin/perl
;这可以追溯到 Perl 通常没有默认安装的时代。)
一个小点:这个#!/usr/bin/env
技巧可以说是对命令的滥用env
,它最初的目的是(顾名思义)在改变的环境中调用命令。此外,一些较旧的系统(包括 SunOS 4,如果我没记错的话)env
没有/usr/bin
.这些都可能不是一个重大问题。 env
确实以这种方式工作,很多脚本确实使用了这个#!/usr/bin/env
技巧,并且操作系统提供商不太可能采取任何措施来破坏它。它可能如果您希望脚本在非常旧的系统上运行,那么这将是一个问题,但无论如何您可能都需要修改它。
另一个可能的问题(感谢 Sopalajo de Arrierez 在评论中指出)是 cron 作业在受限环境中运行。特别$PATH
是,通常类似于/usr/bin:/bin
.因此,如果包含解释器的目录恰好不在这些目录之一中,即使它位于$PATH
用户 shell 中的默认目录中,那么该/usr/bin/env
技巧就不起作用。您可以指定确切的路径,也可以在 crontab 中添加一行进行设置$PATH
(man 5 crontab
了解详细信息)。
Kevin 的评论指出,Pythonvirtualenv
创建了一个特殊情况,其中环境将 Python 解释器安装在插入到$PATH
.对于那个特定的环境(也许其他人也喜欢它),#!/usr/bin/env python
技巧(或python3
?)可能是最好的解决方案。 (我自己没有使用过 virtualenv。)
答案2
因为 /usr/bin/env 可以解释你的$PATH
,这使得脚本更加可移植。
#!/usr/local/bin/python
仅当 python 安装在 /usr/local/bin 中时才会运行您的脚本。
#!/usr/bin/env python
将解释你的$PATH
,并在你的$PATH
.
因此,您的脚本更具可移植性,并且无需修改即可在将 python 安装为/usr/bin/python
、 或/usr/local/bin/python
,甚至自定义目录(已添加到$PATH
)(例如 )的系统上运行/opt/local/bin/python
。
env
可移植性是使用优于硬编码路径的唯一原因。
答案3
目标标准/要求:
在确定是否使用绝对
或者 逻辑的( /usr/bin/env
) 到 shebang 中解释器的路径,有两个关键考虑因素:
A)这口译员可以在目标系统上找到
b)这正确的版本的 口译员 可以在目标系统上找到
要是我们同意那 ”b)“是可取的,我们也同意:
c) 最好是我们的脚本失败而不是使用不正确的解释器版本执行并可能获得不一致的结果。
要是我们不同意那 ”b)“很重要,那么找到任何翻译就足够了。
测试:
由于使用了逻辑的路径 -/usr/bin/env
到 shebang 中的解释器 - 是最可扩展的解决方案,允许相同的脚本在具有相同解释器的不同路径的目标主机上成功执行,我们将使用 Python 来测试它,因为它很受欢迎 - 以确定它是否符合我们的标准。
居住
/usr/bin/env
在可预测的、一致的地点受欢迎的(不是 ”每一个“) 操作系统? 是的:- RHEL 7.5
- 乌班图18.04
- Raspbian 10(“巴斯特”)
- OSX 10.15.02
下面的 Python 脚本在虚拟信封内部和外部执行(皮彭夫使用)在测试期间:
#!/usr/bin/env pythonX.x import sys print(sys.version) print('Hello, world!')
脚本中的 shebang 根据所需的 Python 版本号而变化(全部安装在同一主机上):
#!/usr/bin/env python2
#!/usr/bin/env python2.7
#!/usr/bin/env python3
#!/usr/bin/env python3.5
#!/usr/bin/env python3.6
#!/usr/bin/env python3.7
预期结果:即
print(sys.version)
= 。每次使用不同的已安装 Python 版本执行时,都会打印 shebang 中指定的正确版本。env pythonX.x
./test1.py
测试注意事项:
- 测试仅限于 Python
- Perl 和 Python 一样,
必须
/usr/bin
根据 FHS居住 - 我没有在每种可能数量的 Linuxy/Unixy 操作系统和每个操作系统的版本上测试每种可能的组合。
结论:
尽管确实#!/usr/bin/env python
会使用在用户路径中匹配的第一个 Python 版本,但我们可以通过指定版本号(例如.事实上,开发人员并不关心找到哪个解释器”#!/usr/bin/env pythonX.x
第一的“;他们所关心的是他们的代码是使用他们知道与其代码兼容的指定解释器执行的,以确保一致的结果 -
文件系统中的任何位置...
在便携性/灵活性方面,使用逻辑的- /usr/bin/env
- 而不是绝对根据我对不同版本的测试,路径不仅满足 a)、b) 和 c) 的要求Python,而且还具有模糊逻辑找到相同版本解释器的好处,即使它们位于不同操作系统上的不同路径。
虽然最多发行版尊重 FHS,但并非所有发行版都这样做。
那么,脚本将在哪里失败如果二进制文件位于不同的位置绝对比 shebang 中指定的路径,使用相同的脚本逻辑的小路成功 它会一直持续下去,直到找到匹配项,从而提供更高的跨平台可靠性和可扩展性。
答案4
特别是对于 perl,使用它#!/usr/bin/env
是一个坏主意,原因有两个。
首先,它不便携。在一些不起眼的平台上,env 不在 /usr/bin 中。其次,如基思·汤普森已经指出,它可能会导致在 shebang 线上传递参数时出现问题。最大便携性的解决方案是这样的:
#!/bin/sh
exec perl -x "$0" "$@"
#!perl
有关其工作原理的详细信息,请参阅“perldoc perlrun”以及它对 -x 参数的说明。