“#!/ usr / bin / env bash”和“#!/ usr / bin / bash”之间有什么区别?


371

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

  1. #!/usr/bin/env bash

  2. #!/usr/bin/bash

当查阅env 手册页时,得到以下定义:

 env - run a program in a modified environment

这是什么意思?



6
谁能告诉我为什么这个问题是封闭的,“与编程或软件开发有关”不是?
tarrsalah 2013年

1
我同意,这不是题外话,但它可能是其他一些问题重复,如这一个
基思·汤普森

16
这个问题不应该被标记为题外话。它只需要5个得分在3000以上的人就可以将其标记为“主题”,并且可以重新打开。这是一个问题-特别是关于编程。
Danijel-James W

3
我感到震惊。震惊地发现Linux文档充斥着重言式。xkcd.com/703 git-man-page-generator.lokaltog.net
allyourcode

Answers:


323

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

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

缺点是,/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脚本,而不必重新编写可执行代码行)。但是在以安全性为重点的情况下,后者是首选,因为它限制了代码注入的可能性。


22
另一个缺点是您不能将附加参数传递给解释器。
基思·汤普森

1
@KeithThompson:错误的信息。您可以使用/ usr / bin / env将选项传递给基础解释器!
Gaurav Agarwal 2014年

4
@GauravAgarwal:不在我的系统上。仅包含以下一行的脚本:#!/usr/bin/env echo Hello抱怨:/usr/bin/env: echo Hello: No such file or directory。显然,它被视为echo Hello的单个参数/usr/bin/env
基思·汤普森

1
@AndréLaszlo:该env命令当然允许将参数传递给该命令。问题是该#!行的语义,并且取决于内核。最近的Linux内核确实允许类似的东西#!/usr/bin/env command args,但是较旧的Linux内核和其他系统则不允许。
基思·汤普森

1
为什么代码块中有反引号?他们不应该被删除吗?
本杰明·W.

63

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


9
至少您必须知道env在哪里:)。
ᐅdevrimbaris

1
极好的答案。简洁地解释环境的作用,而不是说“根据您的系统配置选择程序”
De Novo

13

无需/usr/bin/bash/使用env命令像中那样显式定义解释器的路径,而是从首次找到解释器的位置搜索并启动解释器。这既有好处也有缺点


在大多数系统上,它们在功能上是相同的,但这取决于bash和env可执行文件的位置。不过,不确定这将如何影响环境变量。
2013年

2
“可以通过提供解释器的完整路径来指定解释器,而无需使用env。问题是,在不同的计算机系统上,确切路径可能不同。通过使用env,可以搜索解释器并将其定位在脚本运行的时间。这使脚本更易于移植,但也增加了选择错误解释器的风险,因为它会在可执行文件搜索路径中的每个目录中搜索匹配项。路径ENV二进制也可能在每个机器的基础不同。“ -维基百科
迈克·克拉克

10

如果shell脚本以开头#!/bin/bash,它们将始终以bashfrom 开头/bin。如果他们然而,随着开始#!/usr/bin/env bash,他们将搜索bash$PATH,然后用他们能找到的第一个开始。

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

当然,您可以下载bash源代码并从头开始构建自己的bash ~/bin,例如将其放置到。而且你还可以修改您$PATH在您的变量.bash_profile文件,包括~/bin为第一个条目(PATH=$HOME/bin:$PATH~不扩大$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保存的系统上$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

通常,经典外壳都应该位于其中,/bin并且如果您出于某种原因不想将其放置在其中,则将符号链接放置在/bin指向它们实际位置的位置上并不是真正的问题(或者/bin本身可能是符号链接),因此我将永远和#!/bin/sh和在一起#!/bin/bash。如果这些不再起作用,那么太多的东西都会崩溃。并不是说POSIX会要求这些位置(POSIX并没有标准化路径名,因此甚至根本没有标准化shebang功能),但是它们是如此普遍,以至于即使系统不提供/bin/sh,它也可能会理解#!/bin/sh并知道该怎么做,可能仅仅是为了与现有代码兼容。

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


4

我发现它很有用,因为当我不了解环境时,在开始编写脚本之前,我正在这样做:

type nodejs > scriptname.js #or any other environment

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.