16

会有什么不同还是只是个人选择?

4

4 回答 4

18

#!<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没有路径将无法工作。

于 2011-04-05T08:20:44.617 回答
4

从历史上看,UNIX 有 2 组二进制文件:

  • /文件系统在启动早期安装。
  • /usr 可能稍后安装,可能运行脚本和程序/来安排安装。示例:某些站点通过从网络挂载 /usr 来节省空间,但您需要先进入网络。示例:它是一个大型本地文件系统,但如果它被损坏,您需要工具fsck来尝试修复它。

因此,/binand /sbin(使用/lib)必须包含一个最小系统,包括至少一个位于 /bin/sh 的 shell、脚本必需品(如/bin/echo/bin/test)、系统工具(如/bin/mountor/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将其视为更常用的推荐形式(示例

于 2019-04-16T11:06:28.217 回答
2

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"

这是行不通的。

于 2012-12-05T11:15:05.337 回答
1

/usr/bin/env是一个软链接/bin/env。本质上,您正在使用/bin/env

于 2011-04-05T08:29:53.513 回答