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



我在很多地方看到过,包括在这个网站上的推荐(什么是首选的Bash shebang?),优先使用#!/usr/bin/env bash而不是#!/bin/bash。我什至看到一个有进取心的人建议使用#!/bin/bash错误的,这样做会丢失 bash 功能。

综上所述,我在严格控制的测试环境中使用 bash,其中流通中的每个驱动器本质上都是单个主驱动器的克隆。我理解可移植性论点,尽管它不一定适用于我的情况。是否有任何其他理由更喜欢#!/usr/bin/env bash而不是替代方案,并且假设可移植性是一个问题,是否有任何理由使用它可能会破坏功能?

#!/usr/bin/envPATH搜索bashbash并不总是/bin,特别是在非Linux系统上。 例如,在我的OpenBSD系统上,它是/usr/local/bin的,因为它是作为可选软件包安装的。

如果你绝对确定bash/bin并且永远都是,那么直接把它放在你的shebang中是没有坏处的——但我建议不要这样做,因为脚本和程序都有超出我们最初认为它们会有的生命。

bash 的标准位置是/bin,我怀疑这在所有系统上都是如此。但是,如果您不喜欢该版本的 bash 怎么办?例如,我想使用 bash 4.2,但我的 Mac 上的 bash 是 3.2.5。

我可以尝试在/bin重新安装 bash,但这可能是一个坏主意。如果我更新我的操作系统,它将被覆盖。

但是,我可以在/usr/local/bin/bash中安装 bash,并将我的 PATH 设置为:

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

现在,如果我指定bash,我不会在/bin/bash得到旧的粗糙的,而是在/usr/local/bin得到更新的,更闪亮的。好!

除了我的 shell 脚本有那个!# /bin/bashshebang。因此,当我运行我的 shell 脚本时,我得到了那个甚至没有关联数组的旧而糟糕的 bash 版本。

使用/usr/bin/env bash将使用在我的 PATH 中找到的 bash 版本。如果我设置了我的 PATH,以便执行/usr/local/bin/bash,那就是我的脚本将使用的 bash。

在bash 中很少看到这种情况,但在 Perl 和 Python 中更常见:

  • 某些专注于稳定性的Unix/Linux版本有时会落后于这两种脚本语言的发布。不久前,RHEL 的 Perl 是 5.8.8 - 一个八年前的 Perl 版本!如果有人想使用更现代的功能,您必须安装自己的版本。
  • 像Perlbrew和Pythonbrew这样的程序允许你安装这些语言的多个版本。它们依赖于操作您的 PATH 来获取所需版本的脚本。对路径进行硬编码意味着我无法在酿造下运行我的脚本。
  • 不久前(好吧,很久以前),Perl和Python还不是大多数Unix系统中包含的标准包。这意味着您不知道这两个程序的安装位置。是在/bin下吗?/usr/bin/opt/bin?谁知道呢?使用#! /usr/bin/env perl意味着我不必知道。

现在为什么你不应该使用#! /usr/bin/env bash

当路径在 shebang 中被硬编码时,我必须使用该解释器运行。因此,#! /bin/bash迫使我使用默认安装的 bash 版本。由于 bash 功能非常稳定(尝试在 Python 3.x 下运行 Python 脚本的 2.x 版本),因此我的特定 BASH 脚本不太可能不起作用,并且由于我的 bash 脚本可能被这个系统和其他系统使用,使用非标准版本的 bash 可能会产生不良影响。我很可能想确保 bash 的稳定标准版本与我的 shell 脚本一起使用。因此,我可能想在我的 shebang 中对路径进行硬编码。

有很多系统在/bin中没有 Bash,FreeBSD 和 OpenBSD 仅举几例。如果您的脚本旨在移植到许多不同的 Unice,您可能希望使用#!/usr/bin/env bash而不是#!/bin/bash

请注意,这不适用于sh;对于Bourne兼容的脚本,我只使用#!/bin/sh,因为我认为几乎每个存在的Unix都有sh/bin

对于调用bash来说,这有点矫枉过正。除非你在 ~/bin 中有多个像你自己的二进制文件这样的bash二进制文件,但这也意味着你的代码取决于$PATH有正确的东西。

不过,对于像python这样的事情很方便。有一些包装脚本和环境会导致使用替代python二进制文件。

但是,只要您确定它是您真正想要的二进制文件,使用二进制文件的确切路径就不会丢失任何内容。

#!/usr/bin/env bash

肯定更好,因为它从系统环境变量中找到 bash 可执行路径。

转到您的 Linux shell,然后键入

env

它将打印所有环境变量。

转到您的 shell 脚本并键入

echo $BASH

它将打印您的 bash 路径(根据环境变量列表),您应该使用该路径在脚本中构建正确的 shebang 路径。

你的问题是有偏见的,因为它假设#!/usr/bin/env bash优于#!/bin/bash。这个假设是不正确的,原因如下:

env在两种情况下很有用:

  1. 当有多个版本的解释器不兼容时.
    例如python2/3perl4/5, 或php5/7
  2. 当位置取决于PATH时,例如使用python虚拟环境。

但是 bash 不属于这两种情况中的任何一种,因为:

  1. bash非常稳定,尤其是在Linux和BSD等现代系统上,它们构成了绝大多数bash安装。
  2. 通常只有一个版本的bash安装在/bin.
    在过去的 20+ 年里一直如此,只有非常古老的 unices(没有人再使用)有不同的位置。

因此,通过/usr/bin/env遍历PATH变量对 bash 没有用。

加上这三个好的共鸣来使用#!/bin/bash

  1. 对于系统脚本(不使用sh时),PATH变量可能不包含/bin.
    例如,cron默认为非常严格的/usr/bin:/binPATH,这当然很好,但其他上下文/环境可能由于某种特殊原因不包括/bin
  2. 当用户搞砸了他的PATH,这在初学者中很常见。
  3. 例如,当您调用调用 bash 脚本的 suid 程序时,为了安全起见。您不希望通过完全在用户控制下的PATH变量找到解释器!

最后,有人可能会争辩说,有一个合法的用例是env生成bash:当需要使用 .
将额外的环境变量传递给解释器时#!/usr/bin/env -S VAR=value bash但这与bash无关,因为当你控制 shebang 时,你也控制了整个脚本, 因此,只需在脚本中添加VAR=value,即可避免使用 bash 脚本env引入的上述问题。

我更喜欢将主程序包装在如下所示的脚本中,以检查系统上所有可用的bash。最好对它使用的版本进行更多控制。

#! /usr/bin/env bash
# This script just chooses the appropriate bash
# installed in system and executes testcode.main
readonly DESIRED_VERSION="5"
declare all_bash_installed_on_this_system
declare bash
if [ "${BASH_VERSINFO}" -ne "${DESIRED_VERSION}" ]
then
found=0
all_bash_installed_on_this_system="$(
awk -F'/' '$NF == "bash"{print}' "/etc/shells"
)"
for bash in $all_bash_installed_on_this_system
do
versinfo="$( $bash -c 'echo ${BASH_VERSINFO}' )"
[ "${versinfo}" -eq "${DESIRED_VERSION}" ] && { found=1 ; break;}
done
if [ "${found}" -ne 1 ]
then
echo "${DESIRED_VERSION} not available"
exit 1
fi
fi
$bash main_program "$@"

通常#!path/to/command会在执行时触发 bash 以将命令路径附加到调用脚本。例

# file.sh
#!/usr/bin/bash
echo hi

./file.sh将启动一个新进程,脚本将像/bin/bash ./file.sh一样执行

现在

# file.sh
#!/usr/bin/env bash
echo hi

将按照/usr/bin/env bash ./file.sh执行,引用 env 的手册页将其描述为:

env - 在修改后的环境中运行程序

因此,env将在其PATH环境变量中查找命令bash,并在单独的环境中执行,在该环境中,环境值可以传递给env,如NAME=VALUE对。

您可以使用不同的解释器(如 python 等)使用其他脚本对此进行测试。

#!/usr/bin/env python
# python commands

相关内容

  • 没有找到相关文章

最新更新