会有什么不同还是只是个人选择?
4 回答
#!<interpreter> <arguments>
尝试运行<interpreter> <arguments>
以读取并运行文件的其余部分。
所以#!/usr/bin/env
意味着必须有一个名为/usr/bin/env
;的程序。
#!/bin/env
意味着必须有一个名为/bin/env
.
有些系统有一个,而没有另一个。
以我的经验,大多数都有/usr/bin/env
,所以#!/usr/bin/env
更常见。
Unix 系统会尝试运行<interpreter>
using execve
,这就是为什么它必须是完整路径,#!env
没有路径将无法工作。
从历史上看,UNIX 有 2 组二进制文件:
/
文件系统在启动早期安装。/usr
可能稍后安装,可能运行脚本和程序/
来安排安装。示例:某些站点通过从网络挂载 /usr 来节省空间,但您需要先进入网络。示例:它是一个大型本地文件系统,但如果它被损坏,您需要工具fsck
来尝试修复它。
因此,/bin
and /sbin
(使用/lib
)必须包含一个最小系统,包括至少一个位于 /bin/sh 的 shell、脚本必需品(如/bin/echo
等/bin/test
)、系统工具(如/bin/mount
or/sbin/mount
和/sbin/fsck
...
因此,在不同的 Unix 中,几乎任何程序都可能是:
- 在 /usr/bin/ 但不在 /bin
- 在 /bin 但不在 /usr/bin
- 在两者中,与符号链接相同
- 两者但不同!例如,某些系统使用
/bin/sh
非常小的外壳(例如dash
)来加快启动速度,但是符号链接/usr/bin/sh
->/usr/bin/bash
(iirc,将 bash 作为“sh”调用会将其置于某种 posix 模式,但它仍然是一个不同的更强大的外壳)。
大多数时候,只需将 $PATH 设置为包括这两个区域。但是某些上下文喜欢#!
或docker exec
需要固定的完整路径。env
因此,使用编写可移植脚本的技巧——env
恰好进行 PATH 查找。
发生了什么变化
这些都是有效的用例,但现代 Linux 也遵循了类似的“需要小用户空间来挂载/恢复主用户空间”的论点/
! 引入了枢轴系统调用initrd
,并且工具不断发展,可以将您需要的部分复制到其中。
/usr 统一
现在,/
vs/usr
可以说失去了它的目的。原则上每个人都可以在一个文件系统上同时使用符号链接,尽管某些特定设置会中断并且必须更改......
请参阅2012 年的https://lwn.net/Articles/483921/以了解此“/usr 统一”理念的概述。例如 Fedora 完成了它:https ://fedoraproject.org/wiki/Features/UsrMove 。许多其他发行版还没有,或者仍在争论它,或者解决一些问题以减少用户。例如看 debian 的准备:https ://wiki.debian.org/UsrMerge 。
/usr/local/、~/.local/等地方
需要 PATH 查找的原因还有很多:
- 并不是所有的 Unix 系统都安装了所有的语言,或者它们的包太旧了。因此,虽然
env
/bin 或 /usr/bin 或两者中总是存在类似的东西,但可能只有(或更喜欢)python 和其他解释器位于 /usr/local 或您的主目录下...... - 许多语言版本/库管理器都有一种在隔离环境中运行的方法,并且经常通过可执行的“垫片”激活此类环境。例如,一个 Python “virtualenv” 目录有一个 bin/python3 二进制文件;因此,当您将该 bin/ 目录添加到您的 PATH 之前,并且如果您运行执行此操作
#!/usr/bin/env python3
的脚本,那么您只需要使用特定于该环境的模块即可。
回到问题,为env
自己使用什么完整路径?
按照相同的逻辑,在具有不同 /usr 的系统中,env
任何地方都可能缺少其本身,因此您无法编写 100.00% 可移植的#!
行。
在实践中,两者都可能起作用。我没有统计数据,但多年来我一直/usr/bin/env
将其视为更常用的推荐形式(示例)
Mikel 的解释很棒,它忽略了一个小事实(这很重要),它只是传递了一个参数,包括所有空格:
#!<Interpreter> <argument>
结果调用:
$ <Interpreter> '<argument>' path_to_calling_script
例如:
$ cat /tmp/test
#!/usr/bin/env python
print "hi"
$ /tmp/test
与调用相同:
$ /usr/bin/env "python" /tmp/test
引号试图表明,如果您添加任何标志或其他值将成为被调用参数的一部分。
#!/bin/bash -c /bin/env python
将被解释为:
$ /bin/bash "-c /bin/env python"
这是行不通的。
/usr/bin/env
是一个软链接/bin/env
。本质上,您正在使用/bin/env