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

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


当前回答

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

Env在两种情况下是有用的:

当解释器的多个版本不兼容时。 例如python 2/3, perl 4/5,或php 5/7 当位置依赖于PATH时,例如在python虚拟环境中。

但bash不属于这两种情况,因为:

bash非常稳定,特别是在Linux和BSD等现代系统上,它们构成了bash安装的绝大多数。 在/bin下通常只安装了一个版本的bash。 在过去的20多年里一直是这样,只有非常老的公寓(没有人再使用了)有一个不同的位置。

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

再加上这三个使用#!/bin/bash:

for system scripts (when not using sh) for which the PATH variable may not contain /bin. For example cron defaults to a very strict PATH of /usr/bin:/bin which is fine, sure, but other context/environments may not include /bin for some peculiar reason. when the user screwed-up his PATH, which is very common with beginners. for security when for example you're calling a suid program that invokes a bash script. You don't want the interpreter to be found via the PATH variable which is entirely under the user's control!

最后,有人可能会争辩说,env生成bash有一个合理的用例:当需要使用#!/usr/bin/env -S VAR=value bash。 但这不是bash的问题,因为当您控制了shebang时,您也控制了整个脚本,所以只需在脚本中添加VAR=value,从而避免了前面提到的env在bash脚本中引入的问题。

其他回答

有很多系统在/bin中没有Bash,比如FreeBSD和OpenBSD。如果你的脚本想要移植到许多不同的unix,你可能需要使用#!/usr/bin/env bash代替#!/bin/bash。

注意,这并不适用于sh;对于兼容bourne的脚本,我只使用#!/bin/sh,因为我认为几乎所有的Unix都在/bin中有sh。

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脚本有这个!因此,当我运行shell脚本时,我得到的是旧而糟糕的bash版本,它甚至没有关联数组。

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

在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. 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. 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.

为什么你不应该使用#!/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.

我更喜欢将主程序包装在如下脚本中,以检查系统上所有可用的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 "$@"
 #!/usr/bin/env bash

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

转到Linux shell并键入

env

它将打印所有环境变量。

转到shell脚本并键入

echo $BASH

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

# !/usr/bin/env在PATH中搜索bash,而bash并不总是在/bin中,特别是在非linux系统中。例如,在我的OpenBSD系统上,它位于/usr/local/bin中,因为它是作为可选包安装的。

如果您绝对确定bash在/bin中,并且将一直在/bin中,那么将它直接放在shebang中也没有什么害处——但是我建议不要这样做,因为脚本和程序都有超出我们最初认为的生命周期。