在Bash脚本的头文件中,这两个语句的区别是什么:

#!/usr/bin/env bash #!/usr/bin/bash

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

 env - run a program in a modified environment

这是什么意思?


当前回答

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

为什么这有用呢?假设您想运行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 ~/bin for example. And you can also modify your $PATH variable in your .bash_profile file to include ~/bin as the first entry (PATH=$HOME/bin:$PATH as ~ will not expand in $PATH). If you now call bash, the shell will first look for it in $PATH in order, so it starts with ~/bin, where it will find your bash. Same thing happens if scripts search for bash using #!/usr/bin/env bash, so these scripts would now be working on your system using your custom bash build.

一个缺点是,这可能会导致意想不到的行为,例如,同一台机器上的相同脚本可能在不同的环境中使用不同的解释器运行,或者使用不同的搜索路径的用户,这会导致各种各样的头痛。

The biggest downside with env is 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 env will search for an interpreter named <interpreter> <arg>. Note that this is not a problem of the env command 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.

它甚至可能有安全隐患,例如,如果sudo没有配置为清理环境或$PATH被排除在清理之外。让我来演示一下:

通常/bin是一个保护良好的地方,只有root才能在那里更改任何内容。但是,您的主目录不是,您运行的任何程序都能够对它进行更改。这意味着恶意代码可以在一些隐藏目录中放置一个假bash,修改您的.bash_profile以将该目录包含在您的$PATH中,因此所有脚本都使用#!/usr/bin/env bash最终会运行这个假bash。如果sudo保留$PATH,您就有大麻烦了。

例如,考虑一个工具创建一个文件~/。邪恶/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 "$@"

让我们创建一个简单的脚本sample.sh:

#!/usr/bin/env bash

echo "Hello World"

概念证明(在sudo保存$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 /bin and if you don't want to place them there for whatever reason, it's really not an issue to place a symlink in /bin that points to their real locations (or maybe /bin itself is a symlink), so I would always go with #!/bin/sh and #!/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/sh and know what to do with it and may it only be for compatibility with existing code.

但是对于更现代的、非标准的、可选的解释器,如Perl、PHP、Python或Ruby,实际上并没有指定它们应该放在哪里。它们可能在/usr/bin中,但也可能在/usr/local/bin中,或者在一个完全不同的层次结构分支中(/opt/…/应用程序/……等)。这就是为什么它们经常使用#!/usr/bin/env XXX shebang语法。

其他回答

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

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

type nodejs > scriptname.js #or any other environment

然后我将文件中的这一行修改为shebang。 我这样做是因为我不总是记得nodejs在我的计算机上的位置- /usr/bin/或/bin/,所以对我来说env非常有用。也许这里面有细节,但这就是我的理由

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

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

为什么这有用呢?假设您想运行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 ~/bin for example. And you can also modify your $PATH variable in your .bash_profile file to include ~/bin as the first entry (PATH=$HOME/bin:$PATH as ~ will not expand in $PATH). If you now call bash, the shell will first look for it in $PATH in order, so it starts with ~/bin, where it will find your bash. Same thing happens if scripts search for bash using #!/usr/bin/env bash, so these scripts would now be working on your system using your custom bash build.

一个缺点是,这可能会导致意想不到的行为,例如,同一台机器上的相同脚本可能在不同的环境中使用不同的解释器运行,或者使用不同的搜索路径的用户,这会导致各种各样的头痛。

The biggest downside with env is 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 env will search for an interpreter named <interpreter> <arg>. Note that this is not a problem of the env command 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.

它甚至可能有安全隐患,例如,如果sudo没有配置为清理环境或$PATH被排除在清理之外。让我来演示一下:

通常/bin是一个保护良好的地方,只有root才能在那里更改任何内容。但是,您的主目录不是,您运行的任何程序都能够对它进行更改。这意味着恶意代码可以在一些隐藏目录中放置一个假bash,修改您的.bash_profile以将该目录包含在您的$PATH中,因此所有脚本都使用#!/usr/bin/env bash最终会运行这个假bash。如果sudo保留$PATH,您就有大麻烦了。

例如,考虑一个工具创建一个文件~/。邪恶/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 "$@"

让我们创建一个简单的脚本sample.sh:

#!/usr/bin/env bash

echo "Hello World"

概念证明(在sudo保存$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 /bin and if you don't want to place them there for whatever reason, it's really not an issue to place a symlink in /bin that points to their real locations (or maybe /bin itself is a symlink), so I would always go with #!/bin/sh and #!/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/sh and know what to do with it and may it only be for compatibility with existing code.

但是对于更现代的、非标准的、可选的解释器,如Perl、PHP、Python或Ruby,实际上并没有指定它们应该放在哪里。它们可能在/usr/bin中,但也可能在/usr/local/bin中,或者在一个完全不同的层次结构分支中(/opt/…/应用程序/……等)。这就是为什么它们经常使用#!/usr/bin/env XXX shebang语法。

通过/usr/bin/env运行命令的好处是查找当前环境中程序的默认版本。

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

一个缺点是,如果您希望支持Linux,您将无法传递多个参数(例如,您将无法编写/usr/bin/env awk -f),因为POSIX对如何解释行是模糊的,而Linux解释第一个空格之后的所有内容以表示单个参数。你可以在某些版本的env上使用/usr/bin/env -S来解决这个问题,但是这样脚本就会变得更不容易移植,并且会在最近的系统上崩溃(例如,即使是Ubuntu 16.04,如果不是更高的版本)。

另一个缺点是,由于您没有调用显式可执行文件,因此有可能出现错误和多用户系统安全问题(例如,如果有人设法在您的路径中获取名为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

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