Linux | Bash Shebang 应用注意事项

注:本文为 “Linux | Bash Shebang” 相关应用讨论合辑。
英文引文,机翻未校。
如有内容异常,请看原文。


What is the preferred Bash shebang (“#!”)?

哪种 Bash Shebang(#!)写法更推荐使用?

Is there any Bash shebang objectively better than the others for most uses?
是否存在一种 Bash Shebang 写法,在大多数使用场景下都客观优于其他写法?

  • #!/usr/bin/env bash
  • #!/bin/bash
  • #!/bin/sh
  • #!/bin/sh -
  • etc

I vaguely recall a long time ago hearing that adding a dash to the end prevents someone passing a command to your script, but can’t find any details on that.
我隐约记得很久以前听说过,在 Shebang 末尾添加短横线可以防止他人向脚本传入恶意命令,但目前找不到相关细节说明。

edited Sep 4, 2022 at 19:56
Rob Bednark

asked Apr 29, 2012 at 21:37
bgibson

  • 13
    And its /usr/local/bin/bash on OpenBSD.
    在 OpenBSD 系统中,bash 的路径是 /usr/local/bin/bash

    – jww
    CommentedMar 5, 2016 at 1:47

  • 8
    Adding the dash is meant to prevent a certain kind of setuid root spoofing attacks, see
    添加短横线的目的是防范特定类型的 SetUID 根权限欺骗攻击,详情参见:
    security.stackexchange.com/questions/45490/…
    – Vladislav Ivanishin
    CommentedAug 16, 2021 at 16:04

  • 22
    I would upvote this, but it has a score of 1337 and I don’t want to disturb it!
    我本想给这个回答点赞,但它的评分正好是 1337,我不想破坏这个数字!
    – Thomas G Henry LLC
    CommentedFeb 3, 2022 at 18:24

  • 3
    #!/usr/bin/env bash poses a privilege escalation security threat when a suid program executes a bash script that has such a shebang. The user can simply manipulate his PATH and get an arbitrary bash executable to be run instead, with elevated privileges.
    当一个 SetUID 程序执行采用 #!/usr/bin/env bash 作为 Shebang 的 Bash 脚本时,该写法会带来权限提升的安全风险。用户只需操纵自身的 PATH 环境变量,就能让脚本以高权限执行任意指定的 bash 可执行文件。
    – Eric
    CommentedMay 21, 2022 at 22:38

  • 1
    Note that the answer is very dependent on whether or not your script actually needs to be a bash script (relying on bash extensions) or if any (POSIX) sh would do.
    需要注意的是,选择哪种 Shebang 写法,很大程度上取决于你的脚本是否真的需要是 Bash 脚本(即是否依赖 Bash 的扩展语法),还是使用任意一款符合 POSIX 标准的 sh 解释器即可运行。
    – mtraceur
    CommentedDec 27, 2022 at 13:17

Answers

回答内容

You should use #!/usr/bin/env bash for portability: Different *nixes put bash in different places, and using /usr/bin/env is a workaround to run the first bash found on the PATH.
And sh is not bash.
为了保证可移植性,你应当使用 #!/usr/bin/env bash:不同类 Unix 系统会将 bash 安装在不同路径下,而使用 /usr/bin/env 可以作为一种兼容方案,自动调用 PATH 环境变量中找到的第一个 bash 解释器。另外需要明确的是,sh 并不是 bash

edited May 23, 2023 at 13:42
Matthias Braun
answered Apr 30, 2012 at 12:14
l0b0

Comments

bgibson
Thanks. Also looks like adding - to the end of $!/usr/bin/env bash - won’t do anything since only one argument is allowed by *nix in the shebang, and that is used by ‘bash’. That’s apparently only useful for preventing malicious arguments being passed to the script on the commandline if the script’s shebang is one of the others with no arguments (/bin/sh, etc).
bgibson:
感谢解答。另外我发现,在 #!/usr/bin/env bash 末尾添加短横线写成 #!/usr/bin/env bash - 并没有实际作用,因为类 Unix 系统规定 Shebang 中仅允许传递一个参数,且该参数会被 bash 占用。显然,只有当脚本的 Shebang 采用无参数写法(如 /bin/sh 等)时,添加短横线才有助于防止恶意参数通过命令行传入脚本。

ptierno
@Ray bash doesn’t live in /bin on all systems.
@Ray 并非所有系统的 bash 都安装在 /bin 路径下。

Oylex
Same for me, I just added it to an alias: alias shebang='echo "#!/usr/bin/env bash"', now I just have to open the terminal and type shebang instead of going here.
Oylex:
我也是这么做的,我给它设置了一个别名:alias shebang='echo "#!/usr/bin/env bash"',现在我只需打开终端输入 shebang 就能生成,不用再专门查资料了。

darkfeline
This answer is deceptive. POSIX does not say that env is at /usr/bin/env. It could be at /bin/env or anywhere in fact, as long as it is in the path. It could be at /dummy/env if /dummy is in PATH. Shebang itself is undefined under POSIX, so I could make #!stop toaster start the USB coffee machine and be POSIX compliant. So #!/usr/bin/env bash isn’t particularly better than #!/bin/bash, it could be less portable depending.
darkfeline:
这个回答有误导性。POSIX 标准并未规定 env 必须位于 /usr/bin/env,实际上它可以在任何路径下,只要该路径被加入 PATH 环境变量即可。比如如果 /dummyPATH 中,env 也可以在 /dummy/env 路径下。Shebang 本身在 POSIX 标准中并没有明确定义,因此我甚至可以让 #!stop toaster 这条指令启动 USB 咖啡机,且依然符合 POSIX 标准。所以 #!/usr/bin/env bash 并不见得比 #!/bin/bash 更好,在某些情况下它的可移植性反而更差。

l0b0
@darkfeline Portability isn’t absolute - it is mathematically impossible to make any script that will do the same thing on every platform. As of 2012 through 2018 /usr/bin/env exists on more machines than either of /bin/bash xor /usr/bin/bash, so a script that starts with this line will do the expected thing on as many machines as possible.
l0b0:
@darkfeline 可移植性并非绝对的——从数学角度来说,不可能写出一个能在所有平台上实现相同功能的脚本。2012 至 2018 年的数据显示,配备 /usr/bin/env 的设备数量,要多于仅配备 /bin/bash 或仅配备 /usr/bin/bash 的设备数量,因此采用该写法的脚本,能够在尽可能多的设备上按预期运行

On most but not all systems, I recommend using:
在大部分(但不是全部)系统中,我推荐使用:

#!/bin/bash

It’s not 100% portable (some systems place bash in a location other than /bin), but the fact that a lot of existing scripts use #!/bin/bash pressures various operating systems to make /bin/bash at least a symlink to the main location.
这种写法并非 100% 可移植(有些系统会将 bash 安装在 /bin 以外的路径),但由于大量现有脚本都采用 #!/bin/bash 写法,这就迫使各类操作系统将 /bin/bash 至少设置为指向主安装路径的符号链接。

The alternative of:
也有人推荐另一种写法:

#!/usr/bin/env bash

has been suggested – but there’s no guarantee that the env command is in /usr/bin (and I’ve used systems where it isn’t). Furthermore, this form will use the first instance of bash in the current user’s $PATH, which might not be a suitable version of the Bash shell.
但这种写法无法保证 env 命令一定位于 /usr/bin(我就用过 env 不在该路径的系统)。此外,这种写法会调用当前用户 $PATH 环境变量中找到的第一个 bash,而这个 bash 未必是合适的版本。

(But /usr/bin/env should work on any reasonably modern system, either because env is in /usr/bin or because the system does something to make it work. The system I referred to above was SunOS 4, which I probably haven’t used in about 25 years.)
(不过在所有现代化程度尚可的系统中,/usr/bin/env 写法都能正常工作——要么是因为 env 本身就在 /usr/bin 路径,要么是因为系统会做兼容处理来确保该写法生效。我之前提到的那个特殊系统是 SunOS 4,距今大概已经有 25 年没再用过了。)

If you need a script to run on a system that doesn’t have /bin/bash, you can modify the script to point to the correct location (that’s admittedly inconvenient).
如果需要让脚本在没有 /bin/bash 路径的系统上运行,你可以修改脚本中的 Shebang 指向正确路径(诚然这种做法会比较麻烦)。

I’ve discussed the tradeoffs in greater depth in my answe to this question.
关于这种写法的利弊权衡,我在 这个问题的回答中有更深入的探讨。(见下文)

A somewhat obscure update: One system I use, Termux, a desktop-Linux-like layer that runs under Android, doesn’t have /bin/bash (bash is /data/data/com.termux/files/usr/bin/bash) – but it has special handling to support #!/bin/bash.
补充一个比较冷门的情况:我使用的 Termux 系统(一款运行在 Android 上的类桌面 Linux 环境),本身没有 /bin/bash 路径(其 bash 路径为 /data/data/com.termux/files/usr/bin/bash),但该系统做了特殊处理,以支持 #!/bin/bash 写法。

UPDATE: As Edward L. points out in a comment, bash is not part of the “base OS” on FreeBSD, and even if it’s installed by default, it probably won’t be installed as /bin/bash. On such a system, you can either use the #!/usr/bin/env trick (I’m assuming that FreeBSD installed env as /usr/bin/env), or you can use the path where bash is installed (apparently that’s #!/usr/local/bin/bash). If your scripts are only intended to run under FreeBSD, you can use #!/usr/local/bin/bash. If they’re meant to be portable, you can use the #!/usr/bin/env trick (which has some disadvantages; see my answer cited above) or you can update the #! line when you install your scripts.
更新:正如 Edward L. 在评论中指出的,bash 并非 FreeBSD 系统的“基础操作系统组件”,即便它默认被安装,其路径也大概率不是 /bin/bash。在这类系统上,你可以选择两种方案:一是使用 #!/usr/bin/env 兼容写法(我假设 FreeBSD 系统的 env 位于 /usr/bin/env);二是直接使用 bash 的实际安装路径(显然是 #!/usr/local/bin/bash)。如果脚本仅用于 FreeBSD 系统,可直接使用 #!/usr/local/bin/bash;如果需要保证可移植性,则可以使用 #!/usr/bin/env 写法(该写法存在一些弊端,详见我之前引用的回答),或在安装脚本时手动修改 #! 行内容。

There may well be similar issues on some other operating systems.
其他部分操作系统也可能存在类似情况。

edited Feb 20, 2024 at 20:28
answered Oct 17, 2018 at 17:49
Keith Thompson

Comments

Software Engineer
2-years later and this is still the best advice here. If the simple solution doesn’t work then you’ve got to question your earlier decisions. The accepted and most upvoted answer isn’t wrong, it’s just not right
Software Engineer:
两年过去了,这依然是这里最实用的建议。如果简单的方案行不通,那你就得重新审视之前的决策了。目前获赞最高且被采纳的回答不能算错,只是并非完全准确而已

mtraceur
Termux has special handling for #/use/bin/env too, right?
Termux 系统对 #!/usr/bin/env 写法也做了特殊处理,对吧?

Keith Thompson
@mtraceur Yes, it does – and I just discovered that the special handling works on the command line, not just on a #! line.
Keith Thompson:
@mtraceur 是的,而且我刚刚发现,这种特殊处理不仅适用于 #! 行,在命令行中同样有效。

Seamus
This works, but the reason it works (at least on Debian-based systems) is because /bin has only the following symlink: lrwxrwxrwx 1 root root 7 Jul 4 00:04 /bin -> usr/bin. IOW, I still feel the most upvoted answer here is the “best” answer.
这种写法确实有效,但究其原因(至少在基于 Debian 的系统中),是因为 /bin 目录下存在这样一条符号链接:lrwxrwxrwx 1 root root 7 Jul 4 00:04 /bin -> usr/bin。换句话说,我依然认为目前获赞最高的回答才是“最优解”。

mattmc3
If you’re planning to run bash scripts on MacOS, this is bad advice. #!/bin/bash will be perpetually stuck at 3.2.57, which is from 2014. Using #!/bin/bash only works when you’re sure you’re deploying your script to a system that keeps its system bash modern. #!/usr/bin/env bash allows the user running your script have control of pointing to a modern userland bash, instead of a fixed and possibly outdated system bash. Otherwise, you’ll have to limit your scripts to POSIX sh (which really is ideal for portability anyway), or only older bash features.
如果你的脚本要在 macOS 系统上运行,那这个建议就不太适用了。macOS 系统的 /bin/bash 版本会一直停留在 3.2.57(该版本发布于 2014 年)。只有当你确定脚本的部署目标系统会保持系统自带 bash 版本较新时,#!/bin/bash 写法才适用。而 #!/usr/bin/env bash 写法则允许运行脚本的用户,自行指定使用现代的用户态 bash 版本,而非系统中固定且可能过时的 bash 版本。否则,你就只能将脚本限制在 POSIX sh 标准语法范围内(这种写法其实才是可移植性的理想选择),或者仅使用 bash 的旧版特性。

/bin/sh is usually a link to the system’s default shell, which is often bash but on, e.g., Debian systems is the lighter weight dash. Either way, the original Bourne shell is sh, so if your script uses some bash (2nd generation, “Bourne Again sh”) specific features ([[ ]] tests, arrays, various sugary things, etc.), then you should be more specific and use the later. This way, on systems where bash is not installed, your script won’t run. I understand there may be an exciting trilogy of films about this evolution…but that could be hearsay.
/bin/sh 通常是指向系统默认 Shell 的符号链接,这个默认 Shell 很多时候是 bash,但在部分系统(如 Debian)中则是更轻量的 dash。无论哪种情况,最初的 Bourne Shell 都是 sh,因此如果你的脚本使用了 bash(第二代 Shell,全称 “Bourne Again sh”)的特有特性(如 [[ ]] 条件测试、数组、各类语法糖等),就应该明确指定使用 bash 的 Shebang 写法。这样一来,如果目标系统未安装 bash,脚本就不会被错误执行。我听说有一部关于 Shell 演化史的精彩三部曲电影……不过这可能只是传闻。

Also note that when evoked as sh, bash to some extent behaves as POSIX standard sh (see also the GNU docs about this).
另外需要注意的是,当 bashsh 模式被调用时,其行为在一定程度上会符合 POSIX 标准(Bash-POSIX-Mode)。

edited Sep 20, 2018 at 11:15
answered Apr 29, 2012 at 21:39
wholerabbit

Comments

jww
The Public Domain Korn Shell (pdksh) is default on OpenBSD.
OpenBSD 系统的默认 Shell 是公共领域 Korn Shell(pdksh)。

aij
Most systems will not link /bin/sh to anywhere in /usr as that would make it rather hard for the init scripts to run before /usr is mounted.
大部分系统不会/bin/sh 链接到 /usr 目录下的任何路径,因为这样会导致 /usr 分区挂载完成前,初始化脚本难以正常运行。

wholerabbit
@aij I don’t know why I put “many or most” there – I’m a fedora user, where /bin and /sbin for years have just been symlinks by default, to /usr/bin and /usr/sbin, so in that context /bin/sh is a link to bash and the actual directory is /usr/bin. But I’ll correct the above.
@aij 我也不清楚当时为什么会写“很多或大部分”——我自己是 Fedora 用户,该系统多年来默认将 /bin/sbin 设为指向 /usr/bin/usr/sbin 的符号链接,因此在这个环境下 /bin/sh 是指向 bash 的链接,其实际路径位于 /usr/bin。不过我会修正上面的表述。

38
Using a shebang line to invoke the appropriate interpreter is not just for BASH. You can use the shebang for any interpreted language on your system such as Perl, Python, PHP (CLI) and many others. By the way, the shebang
通过 Shebang 行调用对应解释器的用法,并不仅限于 BASH。你可以为系统中的任意解释型语言设置 Shebang,例如 Perl、Python、PHP(命令行版本)等。顺便提一句,如下 Shebang 写法

#!/bin/sh -

(it can also be two dashes, i.e. --) ends bash options everything after will be treated as filenames and arguments.
(也可以使用两个短横线,即 --)会终止 bash 的参数解析,短横线之后的所有内容都会被视为文件名和参数。

Using the env command makes your script portable and allows you to setup custom environments for your script hence portable scripts should use
使用 env 命令既可以提升脚本的可移植性,又能为脚本配置自定义运行环境,因此可移植脚本应采用如下写法

#!/usr/bin/env bash

Or for whatever the language such as for Perl
对于其他语言同理,例如 Perl 语言可写为

#!/usr/bin/env perl

Be sure to look at the man pages for bash:
请务必查阅 bashenv 的手册页:

man bash

and env:

man env

Note: On Debian and Debian-based systems, like Ubuntu, sh is linked to dash not bash. As all system scripts use sh. This allows bash to grow and the system to stay stable, according to Debian.
注意:在 Debian 及其衍生系统(如 Ubuntu)中,sh 被链接到 dash 而非 bash。Debian 官方表示,这是因为所有系统脚本都使用 sh 解释器,这样的设置既能让 bash 持续迭代更新,又能保障系统稳定性。

Also, to keep invocation *nix like I never use file extensions on shebang invoked scripts, as you cannot omit the extension on invocation on executables as you can on Windows. The file command can identify it as a script.
此外,为了遵循类 Unix 系统的调用习惯,我从不给通过 Shebang 执行的脚本添加文件扩展名——这一点和 Windows 不同,Windows 系统执行可执行文件时不能省略扩展名。你可以使用 file 命令来识别脚本文件的类型。

edited Apr 14, 2017 at 11:00
nbro
answered Oct 26, 2016 at 19:55
Jamie R Robillard Sr.


Why is #!/usr/bin/env bash superior to #!/bin/bash?

为什么 #!/usr/bin/env bash 优于 #!/bin/bash

I’ve seen in a number of places, including recommendations on this site (What is the preferred Bash shebang?), to use #!/usr/bin/env bash in preference to #!/bin/bash. I’ve even seen one enterprising individual suggest using #!/bin/bash was wrong and bash functionality would be lost by doing so.
我在很多地方都看到过推荐——包括本站的相关回答(哪种 Bash Shebang 写法更推荐使用?)——优先使用 #!/usr/bin/env bash 而非 #!/bin/bash。我甚至还看到有观点激进的人提出 #!/bin/bash 写法是错误的,认为使用该写法会导致 bash 的部分功能无法使用。

All that said, I use bash in a tightly controlled test environment where every drive in circulation is essentially a clone of a single master drive. I understand the portability argument, though it is not necessarily applicable in my case. Is there any other reason to prefer #!/usr/bin/env bashover the alternatives and, assuming portability was a concern, is there any reason using it could break functionality?
尽管如此,我使用 bash 的场景是一个严格受控的测试环境,所有在用的驱动器本质上都是同一个主驱动器的克隆副本。我理解可移植性方面的考量,但这一点在我的使用场景中并不一定适用。除了可移植性之外,是否还有其他理由让我们优先选择 #!/usr/bin/env bash 写法?另外,如果确实需要考虑可移植性,使用该写法是否存在导致功能异常的风险?

edited May 23, 2017 at 12:26
asked Feb 6, 2014 at 20:09
spugm1r3
13
It isn’t necessarily better. See [this question and my answer on unix.stackexchange.com. (I’d vote to close this as a duplicate, but I don’t think you can do that across sites.)
这种写法未必更优。可以参考 Unix Stack Exchange 上的这个问题以及我的回答(见下文)。(我本想将这个问题标记为重复问题关闭,但跨站点标记应该是不支持的。)
– Keith Thompson
CommentedFeb 6, 2014 at 20:44

  • 3
    In addition to @zigg’s answer, env may not be located at /usr/bin. Shebang comments are altogether a bad idea IMHO. If your default script interpreter doesn’t handle shebang comments, it is just a comment. However, if you know the script interpreter can handle shebang comments, and you know the path to bash, there is no reason not to invoke it using its absolute path unless the path is too long (unlikely), or you might possibly port the script to a system that doesn’t have bash located in /bin. Then again, the caveats I previously mentioned apply in that case since it involves portability.
    补充一下 @zigg 的回答,env 命令不一定位于 /usr/bin 路径。在我看来,Shebang 注释从根本上来说就不是一种理想的方案。如果系统的默认脚本解释器不支持 Shebang 注释,那它就只是一条普通注释。但如果你确定脚本解释器支持 Shebang 注释,且明确知道 bash 的路径,那么除非路径过长(这种情况可能性不大),或者你可能需要将脚本移植到 bash 不在 /bin 路径的系统,否则没有理由不使用绝对路径调用 bash。当然,在涉及可移植性的情况下,我之前提到的那些注意事项依然适用。
    – user539810
    CommentedFeb 6, 2014 at 20:49

  • 1
    @KeithThompson, thanks for the link. Perhaps my search to an answer before posting the question was a little narrow. My take-away from all this:
    @KeithThompson 感谢提供链接。或许我在提问前的搜索范围有点窄了。我从这些内容中得到的结论是:
    (1) linux/unix/posix/etc… is gray, and
    Linux、Unix、POSIX 等相关技术领域的很多问题并没有绝对标准的答案;
    (2) anyone claiming to absolutely have the right answer absolutely has the right answer for their particular scenario.
    任何声称自己的方案绝对正确的人,其方案也仅适用于他自己的特定场景。
    – spugm1r3
    CommentedFeb 6, 2014 at 20:59

  • 4
    The behavior of many things in POSIX/Unix is well defined. The locations are not always so clear cut. Somethings have to exist like /etc or /bin/sh. bash is an add-on for most Unix like systems. It is only Linux where bash is guaranteed to be in /bin and most likely also linked as /bin/sh. Since Linux became the modern de facto Unix for a lot of people the fact that systems other than Linux might exist has been forgotten. In my own answer below I assumed Linux because you said bash. A lot of the BSD boxes I have worked with did not even have it installed.
    POSIX/Unix 系统中很多功能的行为都是有明确标准的,但各类程序的安装路径却并非总是清晰明确。有些路径是系统必须具备的,比如 /etc/bin/sh。而 bash 对大多数类 Unix 系统来说都属于附加组件。只有在 Linux 系统中,bash 才被保证安装在 /bin 路径,且大概率会被链接为 /bin/sh。由于 Linux 已经成为很多人眼中现代 Unix 的事实标准,大家很容易忽略 Linux 之外的其他系统的存在。我在下面的回答中默认基于 Linux 系统展开,因为你提到了 bash。我接触过的很多 BSD 系统甚至都没有安装 bash
    – Sean Perry
    CommentedFeb 6, 2014 at 21:26

  • 7
    @Keith - In the case of Bash (as opposed to Python in the other questions)… OpenBSD does not have a /bin/bash. Bash is not installed by default. If you want it, you have to pkg install bash. Once installed it is located at /usr/local/bin/bash. There is nothing installed at /bin/bash on OpenBSD. A shebang of #!/bin/bash will error, and #!/usr/bin/env bash will succeed.
    @Keith —— 就 Bash 的情况而言(和其他问题中提到的 Python 不同)……OpenBSD 系统并没有 /bin/bash 路径,bash 也不是默认安装的组件。如果需要使用 bash,必须手动执行 pkg install bash 命令安装。安装完成后,其路径为 /usr/local/bin/bash。OpenBSD 系统的 /bin 路径下根本没有 bash 相关文件。因此采用 #!/bin/bash 写法的脚本会执行报错,而 #!/usr/bin/env bash 写法则可以正常运行。
    – jww
    CommentedMay 12, 2019 at 15:26

Answers

回答内容

#!/usr/bin/env searches PATH for bash, and bash is not always in /bin, particularly on non-Linux systems. For example, on my OpenBSD system, it’s in /usr/local/bin, since it was installed as an optional package.
#!/usr/bin/env 会在 PATH 环境变量中搜索 bash,而 bash 并非总是安装在 /bin 路径——在非 Linux 系统中尤其如此。例如我使用的 OpenBSD 系统,bash 是作为可选包安装的,其路径为 /usr/local/bin

If you are absolutely sure bash is in /bin and will always be, there’s no harm in putting it directly in your shebang—but I’d recommend against it because scripts and programs all have lives beyond what we initially believe they will have.
如果你能绝对确定 bash 位于 /bin 路径,且未来永远不会改变,那么直接在 Shebang 中使用绝对路径也并无不妥——但我依然不推荐这种写法,因为脚本和程序的使用周期往往会超出我们最初的预期。

answered Feb 6, 2014 at 20:12
Mattie B

Comments

Julio Guerra
what about env location ? POSIX does not force it.
env 命令的路径问题怎么解决呢?POSIX 标准并没有强制规定它的位置。

ddekany
@zigg That’s so UN*X… <-: I mean, it’s in their best interest to have a standard location for env, but somehow not to have a standard location (which can just be a soft link) for bash. Not to mention, why then hashbang doesn’t accept just #!bash, and use the PATH, instead of we doing exactly the same with env. Not confusing enough for rookies, I guess.
@zigg 这可太有 Unix 风格了……<-: 我的意思是,为 env 设定一个标准路径明明是很合理的做法,但不知为何,大家却不给 bash 设定标准路径(其实只需要设置一个符号链接就行)。更不用说,既然 Shebang 可以通过 env 实现基于 PATH 的解释器查找,那为什么不直接支持 #!bash 这种写法,让系统自动在 PATH 中搜索呢。我猜,可能是觉得这样对新手来说还不够“绕”吧。

Keith Thompson
@XaviMontero: I’ve used a system where env was in /bin, not in /usr/bin (not sure which one, likely SunOS 4). These days it’s very likely /usr/bin/env will be available, just because of the popularity of the #!/usr/bin/env hack.
@XaviMontero:我用过一个 env 命令位于 /bin 而非 /usr/bin 的系统(具体是哪款系统记不清了,大概率是 SunOS 4)。不过如今,得益于 #!/usr/bin/env 这种写法的广泛使用,绝大多数系统都配备了 /usr/bin/env 路径。

Kevin
@ddekany, at first I agreed with you, but then I thought about differences between bash and env. I would wager that it is much more likely for a system have or support multiple types of shells (sh, csh, tsh, zsh, fish, bash, …) and multiple versions of shells (bash 3 vs bash 4) than it is for there to multiple env programs. …Still true that knowing these differences does not lend well to beginners being able to delve in.
@ddekany 一开始我是赞同你的观点的,但后来我仔细想了想 bashenv 的区别。我敢打赌,一个系统同时存在或支持多种 Shell(如 shcshtshzshfishbash 等)以及多个 Shell 版本(如 bash 3 和 bash 4)的概率,要远高于同时存在多个 env 程序的概率。……不过话说回来,这些技术细节确实让新手很难上手。

MatrixManAtYrService
@Marco The insecure practice here is relying on binaries to behave like you’d expect just because they happen to have a particular filepath. NixOS puts nothing at /bin/bash (or anywhere else with a conventional path) precisely so that applications are forced to be explicit about their dependencies and not just run whatever they find at a conventional spot. Who knows what crazy stuff users have sprinkled around their systems. /usr/bin/env by contrast, lets users configure the PATH to control which versions should run in which circumstances. I’d argue that that’s a more secure way.
@Marco 这种写法的不安全之处在于,它仅仅根据文件路径来假定二进制程序的行为符合预期。NixOS 系统就故意不在 /bin/bash(或其他任何常规路径)放置任何程序,其目的就是强制应用明确声明自身依赖,而不是随意运行在常规路径下找到的程序。毕竟谁也不知道用户会在自己的系统里安装些什么奇奇怪怪的东西。相比之下,/usr/bin/env 写法允许用户通过配置 PATH 环境变量,来控制不同场景下使用的程序版本。我认为这种方式反而更安全。

The standard location of bash is /bin, and I suspect that’s true on all systems. However, what if you don’t like that version of bash? For example, I want to use bash 4.2, but the bash on my Mac is at 3.2.5.
bash 的标准安装路径是 /bin,而且我猜测所有系统都是如此。但如果系统自带的 bash 版本不符合你的需求该怎么办?比如我想使用 bash 4.2 版本,但我的 Mac 电脑上自带的 bash 版本却是 3.2.5。

I could try reinstalling bash in /bin but that may be a bad idea. If I update my OS, it will be overwritten.
我可以尝试将 bash 重新安装到 /bin 路径,但这很可能是个糟糕的主意——因为一旦我更新操作系统,这个路径下的 bash 就会被系统自带版本覆盖。

However, I could install bash in /usr/local/bin/bash, and setup my PATH to:
不过我可以将 bash 安装在 /usr/local/bin/bash 路径,并将 PATH 环境变量配置为:

PATH="/usr/local/bin:/bin:/usr/bin:$HOME/bin"

Now, if I specify bash, I don’t get the old cruddy one at /bin/bash, but the newer, shinier one at /usr/local/bin. Nice!
这样一来,当我在终端输入 bash 命令时,调用的就不再是 /bin/bash 路径下老旧的版本,而是 /usr/local/bin 路径下更新、更完善的版本了。完美!

Except my shell scripts have that !# /bin/bash shebang. Thus, when I run my shell scripts, I get that old and lousy version of bash that doesn’t even have associative arrays.
但问题在于,我的 Shell 脚本使用的 Shebang 是 #!/bin/bash。因此当我运行这些脚本时,调用的依然是那个老旧的 bash 版本——连关联数组功能都没有。

Using /usr/bin/env bash will use the version of bash found in my PATH. If I setup my PATH, so that /usr/local/bin/bash is executed, that’s the bash that my scripts will use.
而使用 /usr/bin/env bash 写法,脚本就会调用 PATH 环境变量中找到的 bash 版本。只要我配置好 PATH 让系统优先执行 /usr/local/bin/bash,我的脚本就会使用这个新版本的 bash。

It’s rare to see this with bash, but it is a lot more common with Perl and Python:
这种情况在 bash 中其实并不多见,但在 Perl 和 Python 语言中却非常普遍:

  • Certain Unix/Linux releases which focus on stability are sometimes way behind with the release of these two scripting languages. Not long ago, RHEL’s Perl was at 5.8.8 – an eight year old version of Perl! If someone wanted to use more modern features, you had to install your own version.
    部分主打稳定性的 Unix/Linux 发行版,其自带的 Perl 和 Python 版本往往会严重滞后。就在不久前,RHEL 系统自带的 Perl 版本还是 5.8.8——这是一个已经发布了 8 年的旧版本!如果用户想要使用较新的语言特性,就必须手动安装新版本。

  • Programs like Perlbrew and Pythonbrew allow you to install multiple versions of these languages. They depend upon scripts that manipulate your PATH to get the version you want. Hard coding the path means I can’t run my script under brew.
    Perlbrew 和 Pythonbrew 这类工具可以让用户安装同一语言的多个版本,它们的工作原理就是通过脚本修改 PATH 环境变量,从而调用用户指定的语言版本。如果在 Shebang 中硬编码路径,就无法在这类工具的环境中运行脚本了。

  • It wasn’t that long ago (okay, it was long ago) that Perl and Python were not standard packages included in most Unix systems. That meant you didn’t know where these two programs were installed. Was it under /bin? /usr/bin? /opt/bin? Who knows? Using #! /usr/bin/env perl meant I didn’t have to know.
    其实就在不算太久以前(好吧,其实已经挺久了),Perl 和 Python 还不是大多数 Unix 系统的标准组件。这就意味着用户根本无法确定这两个程序的安装路径——可能在 /bin/usr/bin?还是 /opt/bin?谁也说不准。而使用 #!/usr/bin/env perl 写法,就不用纠结这个问题了。

And Now Why You Shouldn’t Use #! /usr/bin/env bash

不推荐使用 #!/usr/bin/env bash 的理由

When the path is hardcoded in the shebang, I have to run with that interpreter. Thus, #! /bin/bash forces me to use the default installed version of bash. Since bash features are very stable (try running a 2.x version of a Python script under Python 3.x) it’s very unlikely that my particular BASH script will not work, and since my bash script is probably used by this system and other systems, using a non-standard version of bash may have undesired effects. It is very likely I want to make sure that the stable standard version of bash is used with my shell script. Thus, I probably want to hard code the path in my shebang.
当 Shebang 中硬编码了解释器路径时,脚本就只能使用该路径下的解释器运行。因此 #!/bin/bash 写法会强制脚本使用系统默认安装的 bash 版本。由于 bash 的功能特性非常稳定(这一点和 Python 不同——你试试在 Python 3.x 环境下运行 Python 2.x 脚本就知道了),因此我编写的 bash 脚本几乎不会出现兼容性问题。而且由于我的 bash 脚本可能会在本机和其他系统上运行,使用非标准版本的 bash 可能会导致一些意外情况。因此,为了确保脚本使用稳定的标准版本 bash,我更倾向于在 Shebang 中硬编码路径。

edited Jul 16, 2015 at 21:35
answered Feb 6, 2014 at 21:37
David W.


Why is it better to use “#!/usr/bin/env NAME” instead of “#!/path/to/NAME” as my shebang?

为什么优先使用 #!/usr/bin/env NAME 而非 #!/path/to/NAME 作为 Shebang?

I notice that some scripts which I have acquired from others have the shebang #!/path/to/NAME while others (using the same tool, NAME) have the shebang #!/usr/bin/env NAME.
我注意到,我从别处获取的脚本中,有些使用的 Shebang 是 #!/path/to/NAME,而另一些使用相同工具 NAME 的脚本,Shebang 却写成 #!/usr/bin/env NAME

Both seem to work properly. In tutorials (on Python, for example), there seems to be a suggestion that the latter shebang is better. But, I don’t quite understand why this is so.
这两种写法似乎都能正常工作。但在一些教程中(例如 Python 教程),似乎更推荐使用后一种 Shebang 写法。我不太理解其中的原因。

I realize that, in order to use the latter shebang, NAME must be in the PATH whereas the first shebang does not have this restriction.
我明白,使用后一种写法的前提是 NAME 必须在 PATH 环境变量中,而前一种写法则没有这个限制。

Also, it appears (to me) that the first would be the better shebang, since it specifies precisely where NAME is located. So, in this case, if there are multiple versions of NAME (e.g., /usr/bin/NAME, /usr/local/bin/NAME), the first case specifies which to use.
而且在我看来,前一种写法似乎更优,因为它明确指定了 NAME 程序的位置。这样一来,如果系统中存在多个版本的 NAME(例如 /usr/bin/NAME/usr/local/bin/NAME),前一种写法可以精确指定要使用的版本。

My question is why is the first shebang preferred to the second one?
我的问题是,为什么第二种 Shebang 写法反而比第一种更受推崇?

edited Jul 16, 2015 at 0:23
Eric Renouf
asked Jan 21, 2012 at 1:06
TheGeeko61

@TheGeeko61: In my case I had something broken and some variables wasn’t in env. So I suggest to use this shebang to verify if env is correctly loaded.
@TheGeeko61:我之前遇到过一个问题,程序运行异常,部分变量没有出现在环境变量中。因此我建议可以用这种 Shebang 写法来验证环境变量是否被正确加载。
– Cybercartel
CommentedJan 21, 2012 at 10:52

Answers

It isn’t necessarily better.
这种写法并非绝对更优。

The advantage of #!/usr/bin/env python is that it will use whatever python executable appears first in the user’s $PATH.
#!/usr/bin/env python 的优势在于,它会使用用户 $PATH 环境变量中最先出现的 python 可执行文件。

The disadvantage of #!/usr/bin/env python is that it will use whatever python executable appears first in the user’s $PATH.
#!/usr/bin/env python劣势也正在于此:它会使用用户 $PATH 环境变量中最先出现的 python 可执行文件。

That means that the script could behave differently depending on who runs it. For one user, it might use the /usr/bin/python that was installed with the OS. For another, it might use an experimental /home/phred/bin/python that doesn’t quite work correctly.
这意味着脚本的运行行为可能会因执行用户的不同而产生差异。对于某一个用户来说,脚本可能调用系统预装的 /usr/bin/python;而对于另一个用户来说,脚本可能调用一个尚在测试中的 /home/phred/bin/python,这个版本的 Python 可能存在功能缺陷。

And if python is only installed in /usr/local/bin, a user who doesn’t have /usr/local/bin in $PATH won’t even be able to run the script. (That’s probably not too likely on modern systems, but it could easily happen for a more obscure interpreter.)
此外,如果 python 仅安装在 /usr/local/bin 目录下,那么没有将该目录加入 $PATH 的用户将无法运行这个脚本。(这种情况在现代系统中可能不太常见,但对于一些小众的解释器来说,这种问题很容易出现。)

By specifying #!/usr/bin/python you specify exactly which interpreter will be used to run the script on a particular system.
通过写成 #!/usr/bin/python,你可以精确指定某一特定系统上运行脚本时要使用的解释器。

Another potential problem is that on many systems, the #!/usr/bin/env trick doesn’t let you pass arguments to the intrepreter (other than the name of the script, which is passed implicitly). This usually isn’t an issue, but it can be. Many Perl scripts are written with #!/usr/bin/perl -w, but use warnings; is the recommended replacement these days. Csh scripts should use #!/bin/csh -f – but csh scripts are not recommended in the first place. But there could be other examples.
另一个潜在问题是,在许多系统中,#!/usr/bin/env 这种写法无法向解释器传递参数(脚本名称除外,它会被隐式传递)。这一问题通常不会造成影响,但在某些场景下可能引发异常。许多 Perl 脚本会写成 #!/usr/bin/perl -w,不过如今更推荐使用 use warnings; 语句来替代 -w 参数。Csh 脚本则应该写成 #!/bin/csh -f——但首先需要说明的是,并不推荐使用 Csh 编写脚本。当然类似的例子还有很多。

UPDATE: the env of FreeBSD has a -S option (copied by GNU coreutils env in 8.30 with a --split-string alias) that allows passing multiple arguments, so for example #!/usr/bin/env csh -f will fail on systems whose shebang accepts only one argument after the interpreter, but #!/usr/bin/env -S csh -f will work on those such as Linux where what’s after the interpreter is bundled into one argument. Many env implementations such as NetBSD’s, busybox, toybox still don’t support it so that won’t help you write portable scripts.
更新说明:FreeBSD 系统的 env 命令提供了 -S 选项(GNU coreutils 工具集中的 env 命令从 8.30 版本开始借鉴了该功能,并提供 --split-string 作为别名),该选项允许向解释器传递多个参数。例如,#!/usr/bin/env csh -f 在仅支持传递一个参数的系统上会执行失败,而 #!/usr/bin/env -S csh -f 在 Linux 等将解释器后的内容视为单个参数的系统上则可以正常运行。但需要注意的是,许多 env 命令的实现版本(例如 NetBSD、busybox、toybox 中的 env)尚不支持 -S 选项,因此这种写法并不能保证脚本的可移植性。

I have a number of Perl scripts in a personal source control system that I install when I set up an account on a new system. I use an installer script that modifies the #! line of each script as it installs it in my $HOME/bin. (I haven’t had to use anything other than #!/usr/bin/perl lately; it goes back to times when Perl often wasn’t installed by default.)
我在个人代码版本控制系统中存储了一些 Perl 脚本,当我在新系统上创建账号时,会安装这些脚本。我编写了一个安装脚本,在将这些脚本安装到 $HOME/bin 目录时,自动修改每个脚本的 #! 行内容。(我最近已经不再需要使用 #!/usr/bin/perl 之外的写法了;这种兼容写法源于 Perl 尚未成为系统默认安装组件的年代。)

A minor point: the #!/usr/bin/env trick is arguably an abuse of the env command, which was originally intended (as the name implies) to invoke a command with an altered environment. Furthermore, some older systems (including SunOS 4, if I recall correctly) didn’t have the env command in /usr/bin. Neither of these is likely to be a significant concern. env does work this way, a lot of scripts do use the #!/usr/bin/env trick, and OS providers aren’t likely to do anything to break it. It might be an issue if you want your script to run on a really old system, but then you’re likely to need to modify it anyway.
补充一个小细节:严格来说,#!/usr/bin/env 这种写法算是对 env 命令的一种“滥用”——顾名思义,env 命令的设计初衷是在修改后的环境中调用指定命令。此外,一些老旧系统(如果我没记错的话,包括 SunOS 4)的 env 命令并不在 /usr/bin 目录下。不过这两个问题都不太可能引发严重问题。env 命令确实支持这种用法,大量脚本也都在使用 #!/usr/bin/env 写法,操作系统提供商一般不会做出破坏这种兼容性的改动。如果你希望脚本能在非常老旧的系统上运行,这或许会成为一个问题,但即便如此,你很可能也需要对脚本本身进行其他修改。

Another possible issue, (thanks to Sopalajo de Arrierez for pointing it out in comments) is that cron jobs run with a restricted environment. In particular, $PATH is typically something like /usr/bin:/bin. So if the directory containing the interpreter doesn’t happen to be in one of those directories, even if it’s in your default $PATH in a user shell, then the /usr/bin/env trick isn’t going to work. You can specify the exact path, or you can add a line to your crontab to set $PATH (man 5 crontab for details).
另一个可能存在的问题是(感谢 Sopalajo de Arrierez 在评论区指出这一点),定时任务(cron job)会在受限的环境中运行。具体来说,定时任务的 $PATH 环境变量通常只包含 /usr/bin:/bin 这样的默认目录。因此,如果解释器所在的目录不在这些默认目录中,即便它在用户 Shell 的默认 $PATH 里,/usr/bin/env 这种写法也无法正常工作。你可以直接在 Shebang 中填写解释器的绝对路径,或者在定时任务配置文件中添加一行命令来设置 $PATH(详情可参考 man 5 crontab)。

Kevin’s comment points out that Python’s virtualenv creates a special case, where the environment installs a Python interpreter in a special directory that’s inserted at the front of $PATH. For that particular environment (and perhaps others like it), the #!/usr/bin/env python trick (or python3?) is likely to be the best solution. (I haven’t used virtualenv myself.)
Kevin 在评论中提到,Python 的 virtualenv 工具会构成一种特殊场景:该工具会在一个特定目录中安装 Python 解释器,并将这个目录添加到 $PATH 环境变量的最前端。对于这种特定环境(以及其他类似环境),#!/usr/bin/env python(或者写成 python3?)很可能是最佳解决方案。(我本人并未使用过 virtualenv。)

edited Jul 2, 2024 at 6:24
Stéphane Chazelas
answered Jan 21, 2012 at 6:45
Keith Thompson

  • 8
    If /usr/bin/perl is perl 5.8, $HOME/bin/perl is 5.12, and a script requiring 5.12 hardcodes /usr/bin/perl in the shebangs, it can be a major pain to run the script. I’ve rarely seen having /usr/bin/env perl grab perl from the PATH be a problem, but it is often very helpful. And it is much prettier than the exec hack!
    如果 /usr/bin/perl 是 5.8 版本,$HOME/bin/perl 是 5.12 版本,而一个依赖 5.12 版本的脚本却在 Shebang 中硬编码了 /usr/bin/perl,那么运行这个脚本会变得非常麻烦。我几乎没遇到过 /usr/bin/env perlPATH 中查找 Perl 解释器引发的问题,相反这种写法经常能带来便利。而且它比那些通过 exec 实现的技巧写法要优雅得多!
    – William Pursell
    CommentedJan 25, 2012 at 20:30

  • 15
    It’s worth noting that, if you want to use a specific interpreter version, /usr/bin/env is still better. simply because there usually are multiple interpreter versions installed on your machine named perl5, perl5.12, perl5.10, python3.3, python3.32, etc. and if your app has only been tested on that specific version, you can still specify #!/usr/bin/env perl5.12 and be okay even if the user has it installed somewhere unusual. In my experience, ‘python’ is usually just a symlink to the system standard version (not necessarily the most recent version on the system).
    需要注意的是,如果你想使用特定版本的解释器,/usr/bin/env 写法依然是更优选择。原因很简单:你的机器上通常会安装多个版本的解释器,它们的名称可能是 perl5、perl5.12、perl5.10、python3.3、python3.32 等。如果你的应用程序只在某个特定版本下经过测试,你可以写成 #!/usr/bin/env perl5.12,这样即使用户将该版本安装在非标准路径下,脚本也能正常运行。根据我的经验,python 这个名称通常只是一个指向系统标准版本的符号链接(该版本不一定是系统中最新的版本)。
    – root
    CommentedJul 20, 2013 at 19:44

  • 11
    @root: For Perl, use v5.12; serves some of that purpose. And #!/usr/bin/env perl5.12 will fail if the system has Perl 5.14 but not 5.12. For Python 2 vs. 3, #!/usr/bin/python2 and #!/usr/bin/python3 are likely to work.
    @root:对于 Perl 语言来说,use v5.12; 语句可以在一定程度上实现版本控制的目的。但如果系统中安装的是 Perl 5.14 而非 5.12,那么 #!/usr/bin/env perl5.12 这种写法就会失效。对于 Python 2 和 Python 3 的区分,写成 #!/usr/bin/python2#!/usr/bin/python3 通常是可行的。
    – Keith Thompson
    CommentedJul 20, 2013 at 21:44

  • 8
    If it doesn’t work with /usr/bin/perl, I’ll find out very quickly, and it’s the system owner/administrator’s responsibility to keep it up to date. If you want to run my script with your own perl, feel free to grab and modify a copy or invoke it via perl foo. (And you might consider the possibility that the 55 people who upvoted this answer also know a thing or two. It’s certainly possible that you’re right and they’re all wrong, but that’s not the way I’d bet.)
    如果脚本在 /usr/bin/perl 环境下无法运行,我会很快发现这个问题,而保持解释器版本更新是系统所有者或管理员的责任。如果你希望用自己的 Perl 版本运行我的脚本,完全可以复制一份脚本并修改 Shebang,或者通过 perl foo 这种方式调用脚本。(你或许可以想想,给这个回答点赞的 55 个人,大概率也不是外行。当然,你有可能是对的,而他们都是错的,但我并不这么认为。)
    —— Keith Thompson– Keith Thompson
    CommentedDec 14, 2013 at 22:03

  • 8
    This answer is not congruent with how Python in particular is commonly used. The Python executable you want to use is rarely /usr/bin/python. Typically, the one you want lives in a Virtualenv, and is at the front of the user’s $PATH. In the rare situations where that is not the case, /usr/bin will usually be on the user’s $PATH anyway.
    这个回答与 Python 语言的实际常用场景并不相符。在大多数情况下,你真正想使用的 Python 可执行文件并不是 /usr/bin/python。通常来说,目标 Python 解释器位于 虚拟环境(Virtualenv) 中,并且该虚拟环境的目录会被添加到用户 $PATH 环境变量的最前端。即便在少数不使用虚拟环境的场景下,/usr/bin 目录通常也会包含在用户的 $PATH 中。
    – Kevin
    CommentedDec 26, 2015 at 23:31

130
Because /usr/bin/env can interpret your $PATH, which makes scripts more portable.
因为 /usr/bin/env 能够解析你的 $PATH 环境变量,这会让脚本具备更强的可移植性。

#!/usr/local/bin/python

Will only run your script if python is installed in /usr/local/bin.
只有当 Python 安装在 /usr/local/bin 目录下时,这个脚本才能正常运行。

#!/usr/bin/env python

Will interpret your $PATH, and find python in any directory in your $PATH.
这种写法会解析你的 $PATH 环境变量,在 $PATH 包含的任意目录中查找 Python 解释器。

So your script is more portable, and will work without modification on systems where python is installed as /usr/bin/python, or /usr/local/bin/python, or even custom directories (that have been added to $PATH), like /opt/local/bin/python.
因此你的脚本会具备更强的可移植性,无需修改即可在不同系统上运行——无论 Python 被安装在 /usr/bin/python/usr/local/bin/python,还是 /opt/local/bin/python 这类自定义目录(已添加到 $PATH)中。

Portability is the only reason using env is preferred to hard coded paths.
相比于硬编码路径,使用 env 写法更受推崇的唯一原因就是可移植性。

answered Jan 21, 2012 at 1:35
Tim Kennedy

  • 31
    Custom directories for python executables are particularly common as virtualenv usage increases.
    随着 virtualenv 工具使用越来越广泛,将 Python 可执行文件放在自定义目录的场景也变得尤为常见。
    – Xiong Chiamiov
    CommentedNov 22, 2013 at 0:01

  • 2
    What about #! python, why isn’t that used?
    #! python 这种写法呢?为什么没人用?
    – kristianp
    CommentedMay 14, 2018 at 23:54

  • 11
    #! python isn’t used because you’d have to be in the same directory as the python binary, since the bareword python is interpreted as the complete path to the file. If you don’t have a python binary in the current directory, you’ll get an error like bash: ./script.py: python: bad interpreter: No such file or directory. It’s the same as if you used #! /not/a/real/path/python
    没人用 #! python 这种写法,是因为系统会将单独的 python 这个词解析为程序的完整路径,这就意味着你必须在 Python 可执行文件所在的目录下才能运行脚本。如果当前目录中没有 Python 可执行文件,你会收到类似 bash: ./script.py: python: bad interpreter: No such file or directory 的错误提示。这种写法的效果和 #! /not/a/real/path/python 是一样的。
    – Tim Kennedy
    CommentedMay 16, 2018 at 16:14

  • 2
    @TimKennedy uname -a says: Linux silvius 5.6.0-2-amd64 #1 SMP Debian 5.6.14-1 (2020-05-23) x86_64 GNU/Linux, and I’m using zsh. zsh is the key: I have the same failure when running from bash, or with perl -e 'exec "/tmp/foo" or die $!'. TIL, zsh is doing way more work than it has to.
    @TimKennedy 我的系统信息是:Linux silvius 5.6.0-2-amd64 #1 SMP Debian 5.6.14-1 (2020-05-23) x86_64 GNU/Linux,我使用的 Shell 是 zsh。问题的关键就在于 zsh:如果我在 bash 中运行脚本,或者通过 perl -e 'exec "/tmp/foo" or die $!' 这种方式调用,都会遇到同样的错误。今天我才发现,zsh 竟然做了这么多本没必要的工作。
    – Jonathan Klabunde Tomer
    CommentedJun 1, 2020 at 7:15

  • 2
    @TimKennedy aha, from man zsh-misc: "If execution fails because the file is not in executable format, and the file is not a directory, it is assumed to be a shell script. /bin/sh is spawned to execute it. If the program is a file beginning with #!', the remainder of the first line specifies an interpreter for the program. The shell will execute the specified interpreter on operating systems that do not handle this executable format in the kernel." So zsh has implemented its own shebang-line parsing in case it runs on a non-POSIX system, and its implementation differs from Linux kernel's. @TimKennedy 啊,我在 man zsh-misc中找到了答案:“如果文件执行失败,且原因是文件格式不可执行、同时该文件不是目录,则系统会将其视为 Shell 脚本,并启动/bin/sh来执行它。如果该文件以#!` 开头,则首行的剩余部分会指定该程序的解释器。在内核不支持这种可执行文件格式的操作系统上,Shell 会自行执行指定的解释器。” 所以 zsh 实现了一套自己的 Shebang 行解析逻辑,以兼容非 POSIX 标准的系统,而这套逻辑和 Linux 内核的解析逻辑并不相同。
    – Jonathan Klabunde Tomer
    CommentedJun 1, 2020 at 7:30

80

Objective Criteria/Requirements:

客观判定标准与需求

In determining whether to use an absolute or logical (/usr/bin/env) path to an interpreter in a shebang, there are two key considerations:
在决定 Shebang 中使用解释器的绝对路径还是逻辑路径(即 /usr/bin/env 写法)时,需要重点考虑两个核心因素:

a) The interpreter can be found on the target system
目标系统上能够找到对应的解释器

b) The correct version of the interpreter can be found on the target system
目标系统上能够找到对应解释器的正确版本

If we AGREE that “b)” is desirable, we also agree that:
如果我们认同“b) 找到正确版本的解释器”这一需求的重要性,那么我们也应当认同:

c) It’s preferable our scripts fail rather than execute using an incorrect interpreter version and potentially achieve inconsistent results.
与其让脚本在错误的解释器版本下运行并产生不一致的结果,不如让脚本直接执行失败

If we DON’T AGREE that “b)” matters, then any interpreter found will suffice.
如果我们不认同“b) 找到正确版本的解释器”这一需求的重要性,那么只要能找到任意版本的解释器就足够了。

Testing:

测试过程

Since using a logical path — /usr/bin/env to the interpreter in the shebang — is the most extensible solution allowing the same script to execute successfully on target hosts with different paths to the same interpreter, we’ll test it — using Python, due to its popularity — to determine whether it meets our criteria.
由于在 Shebang 中使用解释器的逻辑路径(即 /usr/bin/env 写法)是扩展性最强的方案——它可以让同一个脚本在安装路径不同的目标主机上成功运行——因此我们将以使用广泛的 Python 为例,测试这种写法是否满足上述判定标准。

  1. Does /usr/bin/env live in a predictable, consistent location on POPULAR (not “every”) operating systems? Yes:
    主流(并非所有)操作系统中,/usr/bin/env 是否位于一个可预测、统一的路径下? 答案是肯定的

    • RHEL 7.5
    • Ubuntu 18.04
    • Raspbian 10 (“Buster”)
    • OSX 10.15.02
  2. Below Python script executed both inside and outside of virtual envelopes (Pipenv used) during tests:
    测试过程中,以下 Python 脚本分别在虚拟环境(使用 Pipenv 工具)内和虚拟环境外执行:

    #!/usr/bin/env pythonX.x
    import sys
    print(sys.version)
    print('Hello, world!')
    
  3. The shebang in the script was varied by the Python version number desired (all installed on the same host):
    我们修改脚本 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
  4. Expected results: that print(sys.version) = env python*X*.*x*. Each time ./test1.py was executed using a different installed Python version, the correct version specified in the shebang was printed.
    预期结果:print(sys.version) 语句输出的版本号,应当与 env python*X*.*x* 命令调用的 Python 版本号一致。测试结果显示,每次使用不同版本的 Python 执行 ./test1.py 脚本时,终端都会打印出 Shebang 中指定的正确版本号。

  5. Testing Notes:
    测试说明:

    • Tests were exclusively limited to Python
      测试范围仅限于 Python 语言

    • Perl, like Python, MUST live in /usr/bin according to the FHS
      按照文件系统层次结构标准(FHS)的要求,Perl 和 Python 一样,必须安装在 /usr/bin 目录下

    • I’ve not tested every possible combination on every possible number of Linuxy/Unixy Operating System and version of each Operating System.
      我并未在所有类 Linux / 类 Unix 操作系统及其不同版本中,测试所有可能的组合。

Conclusion:

结论

Although it’s TRUE that #!/usr/bin/env python will use the first version of Python it matches in the user’s Path, we can enforce an express preference by specifying a version number such as #!/usr/bin/env python*X*.*x*. Indeed, developers don’t care which interpreter is found “first”; all they care about is that their code is executed using the specified interpreter they know to be compatible with their code to ensure consistent results — wherever that may live in the filesystem
诚然,#!/usr/bin/env python 写法会使用用户 Path 环境变量中最先匹配到的 Python 版本,但我们可以通过指定版本号(例如 #!/usr/bin/env python*X*.*x*)来强制使用特定版本。事实上,开发者并不关心哪个解释器会被“最先”找到;他们真正关心的是,代码能够在已知兼容的指定解释器版本下运行,从而确保结果的一致性——无论该解释器位于文件系统的哪个位置

In terms of portability/flexibility, using a logical/usr/bin/env — rather than absolute path not only meets requirements a), b) & c) from my testing with different versions of Python, but also has the benefit of fuzzy-logic finding the same version interpreter even if they live at different paths on different Operating Systems. And although MOST distros respect the FHS, not all do.
从可移植性与灵活性的角度来看,使用 /usr/bin/env 这种逻辑路径而非绝对路径的写法,不仅在我针对不同 Python 版本的测试中满足了 a)、b)、c) 三项需求,还具备一个优势:即便同一版本的解释器在不同操作系统中位于不同路径,这种写法也能通过模糊匹配找到目标解释器。(尽管大多数发行版都遵循文件系统层次结构标准,但并非所有发行版都严格遵守该标准:参考链接 https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard#FHS_compliance)。

So, where a script will FAIL if the binary lives in a different absolute path than specified in the shebang, the same script using a logical path SUCCEEDS as it keeps going until it finds a match, thereby offering greater reliability & extensibility across platforms.
因此,当可执行文件的实际路径与 Shebang 中硬编码的绝对路径不一致时,脚本会执行失败;而使用逻辑路径的同一脚本则会执行成功——它会持续在 PATH 中查找,直到找到匹配的解释器。这种写法因此具备更强的跨平台可靠性与扩展性。

edited Mar 13, 2024 at 23:52
G-Man Says ‘Reinstate Monica’
answered Feb 6, 2020 at 1:04
F1Linux

  • 8
    Excellent analysis. We appreciate it.
    分析得非常透彻,感谢分享。
    – TheGeeko61
    CommentedFeb 20, 2020 at 23:55

  • 4
    The question wasn’t specific to Python. I don’t think you can safely assume that other interpreters will be installed as, for example interpX and interpX.x. For example, the system I’m using at the moment has perl, perl5.26.3, and perl5.30.1. Another has perl and perl5.26.1. Neither has a perl5 command. Also, you didn’t mention where the pythonX and pythonX.x interpreters on any of the systems you tested were installed. If they were all in /usr/bin then your experiment doesn’t demonstrate any advantage of #!/usr/bin/env pythonX.x over #!/usr/bin/pythonX.x.
    这个问题并非专门针对 Python 语言。我认为不能想当然地认为其他解释器也会同时存在 interpXinterpX.x 这样的命名版本。例如,我目前使用的系统中安装了 perlperl5.26.3perl5.30.1;另一台系统则安装了 perlperl5.26.1。这两台系统都没有 perl5 这个命令。此外,你并没有说明测试中使用的 pythonXpythonX.x 解释器安装在哪个目录。如果它们都位于 /usr/bin,那么你的实验并不能证明 #!/usr/bin/env pythonX.x 写法比 #!/usr/bin/pythonX.x 更具优势。
    – Keith Thompson

    CommentedFeb 21, 2020 at 2:55

  • 3
    Note that not every Linux system complies to the FHS, not by a long shot. And, paraphrasing the old saying, “Not all the world is Linux”. Many Unix(y) systems have Python, Perl, even bash or tcsh as “optional, unsupported third party additions”, presumably under /usr/local or /opt/*packagename* or some really exotic placements.
    需要注意的是,并非所有 Linux 系统都遵守文件系统层次结构标准,差距还很大。而且套用一句老话:“并非全世界都是 Linux”。许多类 Unix 系统会将 Python、Perl,甚至 bash 或 tcsh 当作“可选的、不受官方支持的第三方组件”,这些程序的安装路径很可能是 /usr/local/opt/*packagename* 或者一些非常特殊的目录。
    – vonbrand
    CommentedFeb 23, 2020 at 4:01

  • 6
    UP VOTED for the formatting* alone, never mind the very useful answer.
    单看这份排版就值得
    点赞*,更不用说内容还这么实用。
    – Teemu Leisti
    CommentedAug 26, 2020 at 13:30

  • 1
    @TeemuLeisti I try to save other technologists the effort of solving the same problems I bump into. So it’s nice to hear somebody found my solution useful, especially when it’s involved a fair amount of testing & documentation such as this one has. Most obliged for your kind words!
    @TeemuLeisti 我只是想帮其他技术从业者省去解决我遇到过的相同问题的麻烦。因此听到有人觉得我的方案有用,我感到非常开心——尤其是这个方案还包含了大量的测试和文档整理工作。非常感谢你的认可!
    – F1Linux
    CommentedAug 26, 2020 at 18:22

58
Specifying the absolute path is more precise on a given system. The downside is that it’s too precise. Suppose you realize that the system installation of Perl is too old for your scripts and you want to use your own instead: then you have to edit the scripts and change #!/usr/bin/perl to #!/home/myname/bin/perl. Worse, if you have Perl in /usr/bin on some machines, /usr/local/bin on others, and /home/myname/bin/perl on yet other machines, then you’d have to maintain three separate copies of the scripts and execute the appropriate one on each machine.
在特定系统上,指定绝对路径的写法更加精确。但这种写法的缺点也在于它过于精确。假设你发现系统预装的 Perl 版本太旧,无法满足脚本运行需求,想要使用自己安装的版本:那么你就必须编辑脚本,将 #!/usr/bin/perl 修改为 #!/home/myname/bin/perl。更糟糕的是,如果有些机器的 Perl 在 /usr/bin,有些在 /usr/local/bin,还有些在 /home/myname/bin/perl,那么你就不得不维护三个不同版本的脚本,并在对应的机器上执行相应版本的脚本。

#!/usr/bin/env breaks if PATH is bad, but so does almost anything. Attempting to operate with a bad PATH is very rarely useful, and indicates that you know very little about the system the script is running on, so you can’t rely on any absolute path anyway.
如果 PATH 环境变量配置有误,#!/usr/bin/env 写法会失效,但几乎所有依赖 PATH 的操作都会受到影响。在 PATH 配置错误的环境中运行程序,这种情况本身就很少见;而且这也说明你对脚本运行的目标系统了解甚少,因此你根本无法依赖任何绝对路径。

There are two programs whose location you can rely on on almost every unix variant: /bin/sh and /usr/bin/env. Some obscure and mostly retired Unix variants had /bin/env without having /usr/bin/env, but you’re unlikely to encounter them. Modern systems have /usr/bin/env precisely because of its widespread use in shebangs. /usr/bin/env is something you can count on.
在几乎所有 Unix 变体系统中,有两个程序的路径是可以信赖的:/bin/sh/usr/bin/env。部分小众且基本已淘汰的 Unix 变体系统,其 env 程序位于 /bin/env 而非 /usr/bin/env,但你几乎不会遇到这类系统。现代系统之所以将 env 程序放置在 /usr/bin/env,正是因为它在 Shebang 中的广泛应用。/usr/bin/env 是一个可以放心使用的路径。

Apart from /bin/sh, the only time you should use an absolute path in a shebang is when your script isn’t meant to be portable, so you can count on a known location for the interpreter. For example, a bash script that only works on Linux can safely use #!/bin/bash. A script that is only meant to be used in-house can rely on house interpreter location conventions.
除了 /bin/sh 之外,唯一适合在 Shebang 中使用绝对路径的场景,是你的脚本不需要具备可移植性,且你明确知道解释器的固定路径。例如,一个仅在 Linux 系统中运行的 bash 脚本,可以安全地使用 #!/bin/bash 写法。一个仅供内部使用的脚本,则可以遵循内部约定的解释器路径来编写 Shebang。

#!/usr/bin/env does have downsides. It’s more flexible than specifying an absolute path but still requires knowing the interpreter name. Occasionally you might want to run an interpreter that isn’t in the $PATH, for example in a location relative to the script. In such cases, you can often make a polyglot script that can be interpreted both by the standard shell and by your desired interpreter. For example, to make a Python 2 script portable both to systems where python is Python 3 and python2 is Python 2, and to systems where python is Python 2 and python2 doesn’t exist:
#!/usr/bin/env 写法确实存在不足之处。它虽然比硬编码绝对路径更灵活,但仍需要你知晓解释器的名称。在某些情况下,你可能需要调用不在 $PATH 中的解释器,例如解释器位于脚本的相对路径下。这种场景下,你通常可以编写一个多语言兼容脚本,让脚本既能被标准 Shell 解析,又能被目标解释器执行。例如,要让一个 Python 2 脚本同时兼容两种系统——一种系统的 python 指向 Python 3、python2 指向 Python 2,另一种系统的 python 指向 Python 2、且没有 python2 命令——可以这样编写脚本:

#!/bin/sh
''':'
if type python2 >/dev/null 2>/dev/null; then
exec python2 "$0" "$@"
else
exec python "$0" "$@"
fi
'''
# real Python script starts here
def …

edited Apr 13, 2017 at 12:37
CommunityBot
answered May 29, 2013 at 23:38
Gilles ‘SO- stop being evil’

29
Specifically for perl, using #!/usr/bin/env is a bad idea for two reasons.
对于 Perl 脚本而言,使用 #!/usr/bin/env 写法并非明智之选,原因有两点。

First, it’s not portable. On some obscure platforms env isn’t in /usr/bin. Second, as Keith Thompson has noted, it can cause trouble with passing arguments on the shebang line. The maximally portable solution is this:
第一,这种写法不具备可移植性。在部分小众平台上,env 程序并不在 /usr/bin 目录下。第二,正如 Keith Thompson 所指出的,这种写法在 Shebang 行传递参数时可能会出现问题。兼容性最佳的解决方案如下:

#!/bin/sh
exec perl -x "$0" "$@"
#!perl

For details on how it works, see ‘perldoc perlrun’ and what it says about the -x argument.
关于该写法的工作原理,可参考 perldoc perlrun 文档中对 -x 参数的说明。

edited Aug 11, 2018 at 16:44
Maximilian Heinzler
answered May 2, 2013 at 15:19
DrHyde

  • 1
    Exactly the answer I was looking for: how to write portable “shebang” for perl script that will allow additional arguments passed to perl (last line in your example accepts additional arguments).
    这正是我要找的答案:如何为 Perl 脚本编写可移植的 Shebang,并且支持向 Perl 解释器传递额外参数(你示例中的最后一行就支持接收额外参数)。
    – AmokHuginnsson
    CommentedMay 25, 2015 at 21:23

  • @AmokHuginnsson it is a bletcherous kludge.
    @AmokHuginnsson 这确实是一种蹩脚的权宜之计。
    – vonbrand
    CommentedFeb 23, 2020 at 4:07

  • 2
    It is, but it’s a bletcherous kludge that works. It’s a blethcherous kludge that exists because less kludgey solutions don’t work.
    确实如此,但这种蹩脚的方案切实有效。正因为更优的方案无法实现,才诞生了这种权宜之计。
    – DrHyde
    CommentedFeb 24, 2020 at 21:59

  • What if sh is not in /bin? True story for google distroless containers.
    如果 sh 程序不在 /bin 目录下该怎么办?谷歌的 distroless 容器就存在这种情况。
    – LLlAMnYP
    CommentedJul 28, 2020 at 16:17

  • 2
    Yeah, it also won’t work if perl isn’t in the path, or on an Amiga, or … If you’re using something weird like that then you’re going to have to do some work yourself.
    没错,如果 Perl 解释器不在 PATH 中,或者在 Amiga 这类系统上,这个方案同样无法运行。如果你使用的是这类特殊环境,就需要自行定制解决方案了。
    – DrHyde
    CommentedJul 29, 2020 at 11:10

20
The reason there is a distinction between the two is because of how scripts are executed.
这两种 Shebang 写法存在差异的根本原因,在于脚本的执行机制不同。

Using /usr/bin/env (which, as mentioned in other answers, is not in /usr/bin on some OSes) is required because you can’t just put an executable name after the #! - it must be an absolute path. This is because the #! mechanism works on a lower level than the shell. It’s part of the kernel’s binary loader. This can be tested. Put this in a file and mark it executable:
必须使用 /usr/bin/env 写法(正如其他回答所提及的,部分操作系统的 env 程序不在 /usr/bin 目录下)的原因是:你不能在 #! 后直接填写可执行文件名称,必须填写绝对路径。这是因为 #! 机制的工作层级低于 Shell,它属于内核二进制加载器的一部分。你可以通过以下方式验证这一点:将以下内容写入文件并标记为可执行:

#!bash

echo 'foo'

You will find it prints an error like this when you attempt to run it:
当你尝试运行该文件时,会收到如下错误提示:

Failed to execute process './test.sh'. Reason:
The file './test.sh' does not exist or could not be executed.

If a file is marked executable and begins with a #!, the kernel (which does not know about $PATH or the current directory: these are user-land concepts) will look for a file using an absolute path. Because using an absolute path is problematic (as mentioned in other answers), someone came up with a trick: You can run /usr/bin/env (which is almost always in that location) to run something using $PATH.
如果一个文件被标记为可执行,且以 #! 开头,内核(内核并不识别 $PATH 或当前目录这类用户态概念)会按照绝对路径去查找对应的程序。由于使用绝对路径存在诸多问题(正如其他回答所提及的),有人想到了一个技巧:可以运行 /usr/bin/env(这个路径几乎在所有系统中都稳定存在),借助它来通过 $PATH 查找并运行目标程序。

answered Jan 19, 2015 at 2:54
Tiffany Bennett

17
There are two more problems with using #!/usr/bin/env
使用 #!/usr/bin/env 写法还存在另外两个问题。

  1. It doesn’t solve the problem of specifying the full path to the interpreter, it just moves it to env.
    这种写法并没有解决“指定解释器完整路径”这一核心问题,只是将路径依赖转移到了 env 程序上。

    env is no more guaranteed to be in /usr/bin/env than bash is guaranteed to be in /bin/bash or python in /usr/bin/python.
    env 程序并不一定就位于 /usr/bin/env,这一点和 bash 不一定在 /bin/bash、Python 不一定在 /usr/bin/python 是同一个道理。

  2. env overwrites ARGV[0] with the name of the interpreter (e.g. bash or python).
    env 程序会将 ARGV[0] 的值替换为解释器的名称(例如 bashpython)。

This prevents your script’s name from appearing in, e.g., ps output (or changes how/where it appears) and makes it impossible to find it with, e.g, ps -C scriptname.sh
这会导致你的脚本名称无法出现在 ps 等命令的输出结果中(或者改变其显示方式和位置),进而无法通过 ps -C scriptname.sh 这类命令查询到脚本进程。

[update 2016-06-04]

And a third problem:
此外还有第三个问题:

  1. Changing your PATH is more work than just editing the first line of a script, especially when scripting such edits is trivial. e.g:
    修改 PATH 环境变量比直接编辑脚本首行要繁琐得多,尤其是编写脚本修改首行的操作非常简单。例如:
printf "%s\n" 1 i '#!'$(type -P python2) . w | ed foo.py

Appending or pre-pending a directory to $PATH is fairly easy (although you still have to edit a file to make it permanent - your ~/.profile or whatever - and it’s far from easy to script THAT edit because the PATH could be set anywhere in the script, not in the first line).
$PATH 中追加或前置一个目录相对简单,但要让这个修改永久生效,你仍然需要编辑配置文件(例如 ~/.profile 等)。而编写脚本完成这个配置文件的修改操作却并不容易,因为 PATH 的定义可能出现在配置文件的任意位置,并非固定在首行。

Changing the order of PATH directories is significantly more difficult…and far harder than just editing the #! line.
调整 PATH 中目录的顺序则要困难得多,远比直接修改 #! 行要复杂。

And you still have all the other problems that using #!/usr/bin/env gives you.
而且你仍然会面临使用 #!/usr/bin/env 写法带来的其他所有问题。

@jlliagre suggests in a comment that #!/usr/bin/env is useful for testing your script with multiple interpreter versions, “by only changing their PATH / PATH order”
jlliagre 在评论中提出,#!/usr/bin/env 写法有助于测试脚本在多个解释器版本下的运行情况,只需“修改 PATH 环境变量或调整其目录顺序”即可。

If you need to do that, it’s much easier to just have several #! lines at the top of your script (they’re just comments anywhere but the very first line) and cut/copy-and-paste the one you want to use now to the first line.
如果你确实需要这样做,更简单的方法是在脚本顶部写入多个 #! 行(除了首行之外,其他位置的 #! 行都会被视为注释),然后将当前需要使用的那一行剪切或复制到首行即可。

In vi, that would be as simple as moving the cursor to the #! line you want, then typing dd1GP or Y1GP. Even with an editor as trivial as nano, it would take seconds to use the mouse to copy and paste.
vi 编辑器中,操作非常简单:将光标移动到目标 #! 行,输入 dd1GPY1GP 命令即可。即便是使用 nano 这类简易编辑器,用鼠标复制粘贴也只需要几秒钟。

Overall, the advantages of using #!/usr/bin/env are minimal at best, and certainly don’t even come close to outweighing the disadvantages. Even the “convenience” advantage is largely illusory.
总而言之,使用 #!/usr/bin/env 写法的优势微乎其微,完全不足以抵消其带来的弊端。甚至连它所谓的“便捷性”优势,在很大程度上也只是一种错觉。

IMO, it’s a silly idea promoted by a particular kind of programmer who thinks that operating systems are not something to be worked with, they are a problem to be worked around (or ignored at best).
在我看来,这种写法是一类程序员鼓吹的不明智方案。这类程序员认为,操作系统不是需要协同工作的工具,而是需要去规避的麻烦(或者说,最好能忽略它的存在)。

PS: here’s a simple script to change the interpreter of multiple files at once.
附:以下是一个可以批量修改多个脚本解释器路径的简单脚本。

change-shebang.sh:

#!/bin/bash

interpreter="$1"
shift

if [ -z "$(type -P $interpreter)" ] ; then
echo "Error: '$interpreter' is not executable." >&2
exit 1
fi

if [ ! -d "$interpreter" ] && [ -x "$interpreter" ] ; then
shebang='#!'"$(realpath -e $interpreter)" || exit 1
else
shebang='#!'"$(type -P $interpreter)"
fi

for f in "$@" ; do
printf "%s\n" 1 i "$shebang" . w | ed "$f"
done

Run it as, e.g., change-shebang.sh python2.7 *.py or change-shebang.sh $HOME/bin/my-experimental-ruby *.rb
你可以这样运行该脚本:change-shebang.sh python2.7 *.py 或者 change-shebang.sh $HOME/bin/my-experimental-ruby *.rb

edited Jun 4, 2016 at 3:17
answered Oct 25, 2015 at 6:18
cas

  • 6
    Nobody else here has mentioned the ARGV[0] problem. And nobody has mentioned the /path/to/env issue in a form that directly addresses one of the arguments for using it (i.e. that bash or perl might be in an unexpected location).
    目前为止,还没有人提到 ARGV[0] 的问题。也没有人以直接回应“bashperl 可能位于非预期路径”这一支持理由的方式,指出 env 程序自身路径的问题。
    – cas
    CommentedOct 25, 2015 at 7:55

  • 3
    The interpreter path issue is easily fixed by a sysadmin with symlinks, or by the user editing their script. It’s certainly not a significant enough problem to encourage people to put up with the all of the other issues caused by using env on the shebang line that are mentioned here and on other questions. That’s promoting a bad solution in the same kind of way that encouraging csh scripting is promoting a bad solution: it kind of works but there are much better alternatives.
    解释器路径的问题很容易解决:系统管理员可以通过创建符号链接来处理,用户也可以直接编辑脚本。这个问题显然没有严重到需要让人们忍受在 Shebang 中使用 env 所带来的其他所有问题——这些问题在本文和其他相关问答中都有提及。这种做法就像推荐使用 csh 编写脚本一样,都是在鼓吹一种糟糕的方案:它虽然勉强能用,但存在好得多的替代方案。
    – cas
    CommentedOct 25, 2015 at 8:37

  • 2
    Not everybody is a sysadmin on their machine. The env solution is aimed at helping non operating system specialists to copy/paste or download scripts that have a good chance to work as is. It is also one of the methods that allow more advanced users to experiment with multiple unchanged scripts to be executed by custom versions of the target interpreter by only changing their PATH / PATH order (sorry about that too long sentence…)
    并非所有人都是自己机器的系统管理员。env 方案的目的,是帮助非操作系统专业人士,让他们复制粘贴或下载的脚本有很大概率可以直接运行。同时,对于高级用户而言,这也是一种可以实现在不修改脚本的前提下,通过调整 PATH 环境变量或其目录顺序,来测试脚本在不同自定义解释器版本下运行情况的方法(抱歉,这句话有点太长了……)。
    – jlliagre
    CommentedOct 25, 2015 at 10:38

  • 3
    non-sysadmins can ask their sysadmin to do it. or they can simply edit the script and change the #! line.
    非系统管理员可以请系统管理员帮忙解决,或者自己直接编辑脚本修改 #! 行即可。
    – cas
    CommentedOct 25, 2015 at 10:51

  • 6
    That is precisely what env is helping to avoid: depending on a sysadmin or OS specific technical knowledge unrelated to python or whatever.
    这正是 env 方案想要避免的情况:避免依赖系统管理员,避免需要掌握与 Python 等编程语言无关的、特定于操作系统的技术知识。
    – jlliagre
    CommentedOct 25, 2015 at 17:27

12
Adding another example here:
这里再补充一个例子:

Using env is also useful when you want to share scripts between multiple rvm environments for example.
例如,当你需要在多个 rvm 环境之间共享脚本时,使用 env 写法会非常方便。

Running this on the cmd line, shows which ruby version will be used when #!/usr/bin/env ruby is used inside a script:
在命令行中运行以下命令,可以查看当脚本使用 #!/usr/bin/env ruby 作为 Shebang 时,系统会调用哪个版本的 Ruby 解释器:

env ruby --version

Therefore, when you use env, you can use different ruby versions through rvm, without changing your scripts.
因此,当你使用 env 写法时,可以通过 rvm 工具切换不同的 Ruby 版本,而无需修改脚本本身。

edited Jan 23, 2012 at 17:54
answered Jan 22, 2012 at 21:35
Not Now

  • 1
    // , Excellent idea. The whole point of multiple interpreters is NOT to break the code, or to have code depend upon that specific interpreter.
    这个想法非常好。使用多个解释器版本的核心意义,就是为了不破坏代码,不让代码依赖于某个特定的解释器版本。
    – Nathan Basanese
    CommentedJan 15, 2016 at 20:52

  • Or maybe you could use different interpreter name if you change the language? If you put env ruby at the front of your script and you actually require some specific version, you’re doing it backwards. The env ruby should mean any version of ruby is okay. Both python and ruby have this same problem where the language syntax or features is different but different people pretend to use the same language and use identical shebang.
    或者,如果你切换了编程语言,是不是应该使用不同的解释器名称?如果你在脚本开头写了 env ruby,但实际上需要某个特定版本的 Ruby,那你的写法就是本末倒置了。env ruby 这种写法的本意应该是“任意 Ruby 版本均可”。Python 和 Ruby 都存在同样的问题:不同版本的语言语法或特性存在差异,但人们却试图用相同的 Shebang 来兼容不同版本,假装它们是同一种语言。
    – Mikko Rantalainen
    CommentedApr 14, 2022 at 11:33


via:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值