编辑: 问题和答案都是正确的,但我在这里提出的问题不是。维护者实际上拒绝用作/usr/bin/env
一种解决方案,而是将脚本重新创建为一个解决sh
方案,这样做会破坏其他用户的安装。因此,用于env
查找bash
不是 POSIX,但默认“足以”被视为标准。
我在 nixos 上安装软件时遇到一些错误(特别是哈斯克尔堆栈)并且在互联网上进行一些挖掘后,我发现了一些人们在安装软件的特定路径方面遇到问题的例子。这里一个示例,维护人员在查找 bash 时恢复了对解决方案的修改,/usr/bin/env
因为它破坏了其他用户的安装。
那么,env
在不告知安装的绝对路径(依赖于系统搜索路径)的情况下引用 shebang 是否可以被认为是错误的?
我的意思是可以使用
#!env bash
代替
#!/usr/bin/env bash
如果安装的绝对路径env
不是所有系统上的默认路径?
答案1
POSIX 没有指定如何准确地解释 shebang。引用自exec
函数族'基本原理:
一种常见的历史实现是执行(),执行(), 执行(), 和执行()对于任何无法识别为可执行文件的文件(包括 shell 脚本),函数都会返回 [ENOEXEC] 错误。当。。。的时候execlp()和execvp()函数遇到这样的文件,它们假设该文件是 shell 脚本并调用已知的命令解释器来解释此类文件。现在 POSIX.1-2008 要求这样做。这些实现execvp()和execlp()仅在命令解释器的可执行文件出现问题的极少数情况下才会给出 [ENOEXEC] 错误。由于这些实现,未提及 [ENOEXEC] 错误execlp() 或者execvp(),尽管实现仍然可以给出它。
一些历史实现处理 shell 脚本的另一种方法是将文件的前两个字节识别为字符串“#!”并使用文件第一行的其余部分作为要执行的命令解释器的名称。
标准开发人员指出的一个潜在的混乱来源是进程映像文件的内容如何影响 exec 系列函数的行为。以下是所采取行动的描述:
如果进程映像文件是该系统的有效可执行文件(采用可执行且有效且具有适当权限的格式),则系统执行该文件。
如果进程映像文件具有适当的权限并且采用可执行但对该系统无效的格式(例如另一个体系结构的可识别二进制文件),那么这是一个错误,并且 错误号设置为 [EINVAL](请参阅后面有关 [EINVAL] 的基本原理)。
如果进程映像文件具有适当的权限但未被识别:
如果这是一个电话execlp()或者execvp(),然后他们调用命令解释器,假设进程映像文件是 shell 脚本。
如果这不是一个电话execlp()或者execvp(),然后发生错误并且错误号设置为 [ENOEXEC]。
而早些时候,在描述,它表示execlp
和execvp
应运行脚本sh
:
在 exec 函数系列的其他成员失败并设置的情况下错误号到 [ENOEXEC],execlp()和execvp() 函数应执行命令解释器,并且执行命令的环境应如同进程调用了嘘 实用程序使用执行()如下:
execl(<shell path>, arg0, file, arg1, ..., (char *)0);
其中 <外壳路径> 是未指定的路径名嘘 utility、file 是进程映像文件,对于execvp(), 在哪里 参数0,精氨酸1等对应于传递给的值execvp()在参数v[0],精液2, 等等。
因此,这些函数的运行相当于sh file ...
.当脚本从 POSIX shell 运行时,shebang 的效果是未指定的。看2.1、外壳介绍:
Zsh 不介意,bash 则介意:
$ cat foo.sh
#! env perl
echo $0
$ zsh -c ./foo.sh
Can't locate object method "echo" via package "./foo.sh" (perhaps you forgot to load "./foo.sh"?) at ./foo.sh line 2.
$ bash -c ./foo.sh
bash: ./foo.sh: env: bad interpreter: No such file or directory
不要依赖于PATH
在 shebang 中未指定查找。 (我知道我运行zsh -c ./foo.sh
而不是zsh foo.sh
,但是,这里的目的是展示 shell 在执行命令时所做的事情可能有所不同。)
答案2
env
几乎存在/usr/bin
于 21 世纪仍然存在的所有类 Unix 系统中。我知道的两个例外是 SCO OpenServer(它仍然为一些非常旧的服务器提供支持)和 NextSTEP(没有太多受支持的硬件尚未消失)。为了便于移植,请使用#!/usr/bin/env
.其他任何东西都不太可移植(除了#!/bin/sh
,它有自己的问题,并且仅当您计划在 SCO 上运行脚本时才有用)。
POSIX 和 Single Unix 不定义/usr/bin/env
、 或,或除、和 中的一些条目/bin/sh
之外的任何绝对路径。不是一个正式的 Unix 标准,但它在很大程度上是一个事实上的标准。/
/tmp
/dev
/usr/bin/env
#!env
完全没用。 Shebang查找不使用PATH
变量,它是由内核完成的。 (如果它做了PATH查找那么你不妨#!bash
直接写)
#!/usr/bin/env bash
对于 bash 脚本来说完全没问题,我质疑拒绝从#!/bin/bash
.看起来像维护者被要求激怒了然后并没有很认真地对待它。更改为使用标准 sh 而不是 bash便携性更好,但是显然那不起作用(该脚本可能还剩下一个非标准构造)。如果无法将脚本转换为在其他 shell 中工作,则脚本仍应更改为使用#!/usr/bin/env bash
而不是#!/bin/bash
.