Linux "#!/usr/bin/env bash" 和 "#!/usr/bin/bash" 有什么区别?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16365130/
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-08-06 22:51:22  来源:igfitidea点击:

What is the difference between "#!/usr/bin/env bash" and "#!/usr/bin/bash"?

linuxbashshellunixshebang

提问by tarrsalah

In the header of a Bash script, what's the difference between those two statements:

在 Bash 脚本的标题中,这两个语句之间有什么区别:

  1. #!/usr/bin/env bash

  2. #!/usr/bin/bash

  1. #!/usr/bin/env bash

  2. #!/usr/bin/bash

When I consulted the envman page, I get this definition:

当我查阅env手册页时,我得到了这个定义:

 env - run a program in a modified environment

What does it mean?

这是什么意思?

采纳答案by Alec Bennett

Running a command through /usr/bin/envhas the benefit of looking for whatever the default version of the program is in your current environment.

运行命令,通过/usr/bin/env具有寻找不管程序的默认版本是在当前的利益ENVironment。

This way, you don't have to look for it in a specific place on the system, as those paths may be in different locations on different systems. As long as it's in your path, it will find it.

这样,您不必在系统上的特定位置查找它,因为这些路径可能位于不同系统上的不同位置。只要它在你的路径上,它就会找到它。

One downside is that you will be unable to pass more than one argument (e.g. you will be unable to write /usr/bin/env awk -f) if you wish to support Linux, as POSIX is vagueon how the line is to be interpreted, and Linux interprets everything after the first space to denote a single argument. You can use /usr/bin/env -Son some versions of envto get around this, but then the script will become even less portable and break on fairly recent systems (e.g. even Ubuntu 16.04 if not later).

一个缺点是,/usr/bin/env awk -f如果您希望支持 Linux,您将无法传递多个参数(例如,您将无法编写),因为POSIX对如何解释该行含糊不清,而 Linux 在第一个空间来表示单个参数。您可以使用/usr/bin/env -S的某些版本env来解决这个问题,但是脚本将变得更不便携并且在相当新的系统上会中断(例如,即使不是更新的 Ubuntu 16.04)。

Another downside is that since you aren't calling an explicit executable, it's got the potential for mistakes, and on multiuser systems security problems (if someone managed to get their executable called bashin your path, for example).

另一个缺点是,由于您没有调用显式可执行文件,因此可能会出错,并且会出现多用户系统安全问题(例如,如果有人设法bash在您的路径中调用了他们的可执行文件)。

#!/usr/bin/env bash #lends you some flexibility on different systems
#!/usr/bin/bash     #gives you explicit control on a given system of what executable is called

In some situations, the first may be preferred (like running python scripts with multiple versions of python, without having to rework the executable line). But in situations where security is the focus, the latter would be preferred, as it limits code injection possibilities.

在某些情况下,第一个可能是首选(例如使用多个版本的 python 运行 python 脚本,而不必返工可执行行)。但在安全性是重点的情况下,后者将是首选,因为它限制了代码注入的可能性。

回答by safay

Instead of explicitly defining the path to the interpreter as in /usr/bin/bash/, by using the env command, the interpreter is searched for and launched from wherever it is first found. This has both upsides and downsides

/usr/bin/bash/通过使用 env 命令,不是像 in 那样明确定义解释器的路径,而是从第一次找到解释器的地方搜索并启动它。这有好处也有坏处

回答by Dolphiniac

Using #!/usr/bin/env NAMEmakes the shell search for the first match of NAME in the $PATH environment variable. It can be useful if you aren't aware of the absolute path or don't want to search for it.

Using#!/usr/bin/env NAME使 shell 在 $PATH 环境变量中搜索 NAME 的第一个匹配项。如果您不知道绝对路径或不想搜索它,它会很有用。

回答by sudo97

I find it useful, because when I didn't know about env, before I started to write script I was doing this:

我觉得它很有用,因为当我不知道 env 时,在我开始编写脚本之前,我是这样做的:

type nodejs > scriptname.js #or any other environment

and then I was modifying that line in the file into shebang.
I was doing this, because I didn't always remember where is nodejs on my computer -- /usr/bin/ or /bin/, so for me envis very useful. Maybe there are details with this, but this is my reason

然后我将文件中的那一行修改为shebang。
我是这样做的,因为我并不总是记得我电脑上的 nodejs 在哪里——/usr/bin/ 或 /bin/,所以对我env来说非常有用。也许这有细节,但这是我的原因

回答by Mecki

If the shell scripts start with #!/bin/bash, they will always run with bashfrom /bin. If they however start with #!/usr/bin/env bash, they will search for bashin $PATHand then start with the first one they can find.

如果 shell 脚本以 开头#!/bin/bash,它们将始终以bashfrom运行/bin。但是#!/usr/bin/env bash,如果他们以 开头,他们将搜索bashin $PATH,然后从他们能找到的第一个开始。

Why would this be useful? Assume you want to run bashscripts, that require bash 4.x or newer, yet your system only has bash3.x installed and currently your distribution doesn't offer a newer version or you are no administrator and cannot change what is installed on that system.

为什么这会有用?假设您要运行bash需要 bash 4.x 或更高版本的脚本,但您的系统仅bash安装了 3.x,并且目前您的发行版不提供更新版本,或者您不是管理员并且无法更改该系统上安装的内容.

Of course, you can download bash source code and build your own bash from scratch, placing it to ~/binfor example. And you can also modify your $PATHvariable in your .bash_profilefile to include ~/binas the first entry (PATH=$HOME/bin:$PATHas ~will not expand in $PATH). If you now call bash, the shell will first look for it in $PATHin order, so it starts with ~/bin, where it will find your bash. Same thing happens if scripts search for bashusing #!/usr/bin/env bash, so these scripts would now be working on your system using your custom bashbuild.

当然,您可以下载 bash 源代码并从头开始构建自己的 bash ~/bin,例如将其放置到。您还可以修改文件中的$PATH变量以将.bash_profile其包含~/bin为第一个条目(PATH=$HOME/bin:$PATH因为~不会在 中扩展$PATH)。如果您现在调用bash,shell 将首先$PATH按顺序查找它,因此它以 开头~/bin,它将在其中找到您的bash. 如果脚本搜索bashusing #!/usr/bin/env bash,也会发生同样的事情,因此这些脚本现在可以使用您的自定义bash构建在您的系统上运行。

One downside is, that this can lead to unexpected behavior, e.g. same script on the same machine may run with different interpreters for different environments or users with different search paths, causing all kind of headaches.

一个缺点是,这可能会导致意外行为,例如,同一台机器上的相同脚本可能会针对不同环境或具有不同搜索路径的用户使用不同的解释器运行,从而导致各种头痛。

The biggest downside with envis that some systems will only allow one argument, so you cannot do this #!/usr/bin/env <interpreter> <arg>, as the systems will see <interpreter> <arg>as one argument (they will treat it as if the expression was quoted) and thus envwill search for an interpreter named <interpreter> <arg>. Note that this is not a problem of the envcommand itself, which always allowed multiple parameters to be passed through but with the shebang parser of the system that parses this line before even calling env. Meanwhile this has been fixed on most systems but if your script wants to be ultra portable, you cannot rely that this has been fixed on the system you will be running.

最大的缺点env是有些系统只允许一个参数,所以你不能这样做#!/usr/bin/env <interpreter> <arg>,因为系统会将其<interpreter> <arg>视为一个参数(他们将把它视为表达式被引用),因此env将搜索名为<interpreter> <arg>. 请注意,这不是env命令本身的问题,它总是允许传递多个参数,但系统的 shebang 解析器甚至在调用env. 同时,这已在大多数系统上修复,但如果您的脚本想要超便携,则不能指望这已在您将运行的系统上修复。

It can even have security implications, e.g. if sudowas not configured to clean environment or $PATHwas excluded from clean up. Let me demonstrate this:

它甚至可能具有安全隐患,例如,如果sudo未配置为清理环境或被$PATH排除在清理之外。让我证明这一点:

Usually /binis a well protected place, only rootis able to change anything there. Your home directory is not, though, any program you run is able to make changes to it. That means malicious code could place a fake bashinto some hidden directory, modify your .bash_profileto include that directory in your $PATH, so all scripts using #!/usr/bin/env bashwill end up running with that fake bash. If sudokeeps $PATH, you are in big trouble.

通常/bin是一个保护良好的地方,只有root在那里才能改变任何东西。但是,您的主目录并不是您运行的任何程序都能够对其进行更改的。这意味着恶意代码可以放置假bash到一些隐藏的目录,修改.bash_profile,包括你的那个目录$PATH,所以使用的所有脚本#!/usr/bin/env bash将结束,假冒运行bash。如果sudo一直这样$PATH,你就有大麻烦了。

E.g. consider a tool creates a file ~/.evil/bashwith the following content:

例如,考虑一个工具创建一个~/.evil/bash包含以下内容的文件:

#!/bin/bash

if [ $EUID -eq 0 ]; then
  echo "All your base are belong to us..."
  # We are root - do whatever you want to do
fi

/bin/bash "$@"

Let's make a simple script sample.sh:

让我们做一个简单的脚本sample.sh

#!/usr/bin/env bash

echo "Hello World"

Proof of concept (on a system where sudokeeps $PATH):

概念证明(在sudokeep的系统上$PATH):

$ ./sample.sh
Hello World

$ sudo ./sample.sh
Hello World

$ export PATH="$HOME/.evil:$PATH"

$ ./sample.sh
Hello World

$ sudo ./sample.sh
All your base are belong to us...
Hello World

Usually the classic shells should all be located in /binand if you don't want to place them there for whatever reason, it's really not an issue to place a symlink in /binthat points to their real locations (or maybe /binitself is a symlink), so I would always go with #!/bin/shand #!/bin/bash. There's just too much that would break if these wouldn't work anymore. It's not that POSIX would require these position (POSIX does not standardize path names and thus it doesn't even standardize the shebang feature at all) but they are so common, that even if a system would not offer a /bin/sh, it would probably still understand #!/bin/shand know what to do with it and may it only be for compatibility with existing code.

通常经典的 shell 都应该放在里面/bin,如果你不想把它们放在那里,无论出于什么原因,在/bin其中放置一个指向它们真实位置/bin的符号链接(或者它本身就是一个符号链接)真的不是问题,所以我总是和#!/bin/sh和一起去#!/bin/bash。如果这些不再起作用,就会有太多的东西会崩溃。并不是 POSIX 需要这些位置(POSIX 没有标准化路径名,因此它甚至根本没有标准化 shebang 功能)但它们是如此常见,即使系统不提供/bin/sh,它可能仍然会理解#!/bin/sh并知道如何处理它,它可能只是为了与现有代码兼容。

But for more modern, non standard, optional interpreters like Perl, PHP, Python, or Ruby, it's not really specified anywhere where they should be located. They may be in /usr/binbut they may as well be in /usr/local/binor in a completely different hierarchy branch (/opt/..., /Applications/..., etc.). That's why these often use the #!/usr/bin/env xxxshebang syntax.

但是对于更现代的、非标准的、可选的解释器,如 Perl、PHP、Python 或 Ruby,并没有真正指定它们应该位于的任何地方。它们可以是/usr/bin,但他们可能也处于/usr/local/bin或完全不同的层级分支(/opt/.../Applications/...,等)。这就是为什么这些经常使用#!/usr/bin/env xxxshebang 语法的原因。