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

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


当前回答

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

如果您绝对确定bash在/bin中,并且将一直在/bin中,那么将它直接放在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

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

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

你的问题是有偏见的,因为它假设#!/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脚本中引入的问题。

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

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