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

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


当前回答

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

其他回答

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

对于调用bash来说,这有点过头了。除非您在~/bin中有多个bash二进制文件,但这也意味着您的代码依赖于$PATH中有正确的内容。

不过,它对于python之类的东西很方便。有一些包装器脚本和环境会导致使用其他的python二进制文件。

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

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

如果您绝对确定bash在/bin中,并且将一直在/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脚本有这个!因此,当我运行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.

通常# !Path /to/命令将触发bash在执行时将命令路径前置到调用脚本。的例子,

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

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

Now

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

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

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

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

你可以使用python等不同的解释器来测试其他脚本。

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