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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21612980/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 00:39:11  来源:igfitidea点击:

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

bashshebang

提问by spugm1r3

I've seen in a number of places, including recommendations on this site (What is the preferred Bash shebang?), to use #!/usr/bin/env bashin preference to #!/bin/bash. I've even seen one enterprising individual suggest using #!/bin/bashwas wrongand 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替代方案,假设便携性是一个问题,是否有任何理由使用它会破坏功能?

回答by zigg

#!/usr/bin/envsearches PATHfor bash, and bashis 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搜索PATHbash,而bash不是总在/bin,特别是在非Linux系统。例如,在我的 OpenBSD 系统上,它在 中/usr/local/bin,因为它是作为可选包安装​​的。

If you are absolutely sure bashis in /binand 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 中并没有什么坏处——但我建议您不要这样做,因为脚本和程序的寿命都超出了我们最初认为的寿命。

回答by David W.

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 /binbut that may be a bad idea. If I update my OS, it will be overwritten.

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

However, I could install bash in /usr/local/bin/bash, and setup my PATH to:

但是,我可以在 bash 中安装 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/bashshebang. Thus, when I run my shell scripts, I get that old and lousy version of bash that doesn't even have associative arrays.

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

Using /usr/bin/env bashwill use the version of bash found in my PATH. If I setup my PATH, so that /usr/local/bin/bashis executed, that's the bash that my scripts will use.

Using/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 stabilityare 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 perlmeant I didn't have to know.
  • 某些专注于稳定性的Unix/Linux 版本有时会落后于这两种脚本语言的发布。不久前,RHEL 的 Perl 是 5.8.8 —— 一个八年前的 Perl 版本!如果有人想使用更现代的功能,您必须安装自己的版本。
  • Perlbrew 和 Pythonbrew 等程序允许您安装这些语言的多个版本。它们依赖于操纵您的 PATH 的脚本来获得您想要的版本。对路径进行硬编码意味着我无法在brew下运行我的脚本。
  • 不久之前(好吧,很久以前)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/bashforces 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 3.x 下运行 2.x 版本的 Python 脚本),因此我的特定 BASH 脚本不太可能无法运行,而且我的 bash 脚本可能已被该系统和其他系统使用,使用非标准版本的 bash 可能会产生不良影响。我很可能想确保 bash 的稳定标准版本与我的 shell 脚本一起使用。因此,我可能想对我的shebang 中的路径进行硬编码。

回答by Sean Perry

For invoking bashit is a little bit of overkill. Unless you have multiple bashbinaries like your own in ~/bin but that also means your code depends on $PATH having the right things in it.

调用bash它有点矫枉过正。除非您bash在 ~/bin 中有多个像您自己的二进制文件,但这也意味着您的代码取决于 $PATH 中是否包含正确的内容。

It is handy for things like pythonthough. There are wrapper scripts and environments which lead to alternative pythonbinaries being used.

不过,这对于诸如此类的事情来说很方便python。存在导致使用替代python二进制文件的包装脚本和环境。

But nothing is lost by using the exact path to the binary as long as you are sure it is the binary you really want.

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

回答by James Ko

There are a lot of systems that don't have Bash in /bin, FreeBSD and OpenBSD just to name a few. If your script is meant to be portable to many different Unices, you may want to use #!/usr/bin/env bashinstead of #!/bin/bash.

有很多系统没有 Bash in /bin、FreeBSD 和 OpenBSD,仅举几例。如果你的脚本是指可以移植到许多不同的Unix系统,你可能需要使用#!/usr/bin/env bash替代#!/bin/bash

Note that this does not hold true for sh; for Bourne-compliant scripts I exclusively use #!/bin/sh, since I think pretty much every Unix in existence has shin /bin.

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

回答by kta

 #!/usr/bin/env bash

is definitely better because it finds the bash executable path from your system environment variable.

绝对更好,因为它可以从您的系统环境变量中找到 bash 可执行文件路径。

Go to your Linux shell and type

转到您的 Linux shell 并键入

env

It will print all your environment variables.

它将打印您所有的环境变量。

Go to your shell script and type

转到您的 shell 脚本并键入

echo $BASH

It will print your bash path (according to the environment variable list) that you should use to build your correct shebang path in your script.

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

回答by Mihir

I would prefer wrapping the main program in a script like below to check all bashavailable on system. Better to have more control on the version it uses.

我更喜欢将主程序包装在如下脚本中以检查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 "$@"