517

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

  1. #!/usr/bin/env bash

  2. #!/usr/bin/bash

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

 env - run a program in a modified environment

这是什么意思?

4

5 回答 5

426

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

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

/usr/bin/env awk -f一个缺点是,如果您希望支持 Linux,您将无法传递多个参数(例如,您将无法编写),因为 POSIX对如何解释该行含糊不清,而 Linux 会在第一个之后解释所有内容空间来表示单个参数。您可以/usr/bin/env -S在某些版本上使用env来解决此问题,但随后脚本将变得更不便携并且在相当新的系统上中断(例如,如果不是更高版本,甚至是 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 脚本,而无需重新编写可执行行)。但是在以安全为重点的情况下,后者将是首选,因为它限制了代码注入的可能性。

于 2013-05-03T18:26:41.030 回答
95

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

于 2013-05-03T18:19:22.647 回答
38

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

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

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

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

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

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

通常/bin是一个保护良好的地方,只能root在那里改变任何东西。但是,您的主目录不是,您运行的任何程序都可以对其进行更改。这意味着恶意代码可能会将假bash文件放入某个隐藏目录中,修改您的目录以.bash_profile将该目录包含在您的. 如果留着,你就麻烦大了。$PATH#!/usr/bin/env bashbashsudo$PATH

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

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

#!/usr/bin/env bash

echo "Hello World"

概念证明(在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

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

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

于 2019-04-30T19:02:23.007 回答
15

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

于 2013-05-03T18:18:44.113 回答
4

我觉得它很有用,因为当我不了解 env 时,在我开始编写脚本之前,我正在这样做:

type nodejs > scriptname.js #or any other environment

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

于 2017-08-14T20:38:09.737 回答