在 Bash 脚本的标题中,这两个语句之间有什么区别:
#!/usr/bin/env bash
#!/usr/bin/bash
当我查阅env
手册页时,我得到了这个定义:
env - run a program in a modified environment
这是什么意思?
运行命令的好处是可以在您当前的环境中查找/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 脚本,而无需重新编写可执行行)。但是在以安全为重点的情况下,后者将是首选,因为它限制了代码注入的可能性。
使用#!/usr/bin/env NAME
使 shell 在 $PATH 环境变量中搜索 NAME 的第一个匹配项。如果您不知道绝对路径或不想搜索它,它会很有用。
如果 shell 脚本以 开头#!/bin/bash
,它们将始终以bash
from运行/bin
。但是,如果他们以 开头#!/usr/bin/env bash
,他们将搜索bash
in $PATH
,然后从他们能找到的第一个开始。
为什么这会有用?假设您要运行bash
需要 bash 4.x 或更新版本的脚本,但您的系统只bash
安装了 3.x,并且当前您的发行版不提供更新版本,或者您不是管理员,无法更改该系统上安装的内容.
当然,您可以下载 bash 源代码并从头开始构建自己的 bash,~/bin
例如将其放置到。您还可以修改文件中的$PATH
变量.bash_profile
以包含~/bin
为第一个条目(不会在 中扩展PATH=$HOME/bin:$PATH
)。如果你现在调用,shell 将首先按顺序查找它,所以它以 开头,它会在哪里找到你的. 如果脚本搜索using也会发生同样的情况,因此这些脚本现在可以使用您的自定义构建在您的系统上运行。~
$PATH
bash
$PATH
~/bin
bash
bash
#!/usr/bin/env bash
bash
一个缺点是,这可能会导致意外行为,例如,同一台机器上的相同脚本可能会针对不同环境使用不同的解释器或具有不同搜索路径的用户运行,从而导致各种令人头疼的问题。
最大的缺点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 bash
bash
sudo
$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"
概念证明(在sudo
keep的系统上$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/sh
and #!/bin/bash
。如果这些不再起作用,就会破坏太多。不是 POSIX 需要这些位置(POSIX 不标准化路径名,因此它甚至根本不标准化 shebang 功能)但它们是如此普遍,即使系统不提供 a /bin/sh
,它可能仍然会理解#!/bin/sh
并知道如何处理它,并且可能只是为了与现有代码兼容。
但是对于更现代的、非标准的、可选的解释器,如 Perl、PHP、Python 或 Ruby,并没有真正指定它们应该位于的任何位置。它们可能位于/usr/bin
但也可能位于/usr/local/bin
或位于完全不同的层次结构分支(/opt/...
、/Applications/...
等)中。这就是为什么这些经常使用#!/usr/bin/env xxx
shebang 语法。
通过使用 env 命令,无需像在 中那样显式定义解释器的路径,而是/usr/bin/bash/
从第一次找到解释器的地方搜索并启动解释器。这既有好处也有坏处
我觉得它很有用,因为当我不了解 env 时,在我开始编写脚本之前,我正在这样做:
type nodejs > scriptname.js #or any other environment
然后我将文件中的那一行修改为shebang。
我这样做是因为我并不总是记得 nodejs 在我的计算机上的位置——/usr/bin/ 或 /bin/,所以对我env
来说非常有用。也许有细节,但这是我的原因