在bash脚本的头部分,这两个语句有什么区别?
#!/usr/bin/env bash
#!/usr/bin/bash
当我试图看到env手册页时,我得到了以下定义:
1
| env - run a program in a modified environment |
这是什么意思?
- 看看这个问题和我的答案。
- 正是我需要的,谢谢@keitththompson。
- 谁能告诉我为什么这个问题是封闭的,"与编程或软件开发相关"是不封闭的?
- 我同意这不是离题,但它可能是其他几个问题的副本,比如这个问题。
- 此问题不应标记为主题外。只需要5个3000分以上的人就可以将其标记为"主题",并且可以重新打开。这是一个问题——特别是关于编程。
- @丹尼尔:我同意这不是离题,但它是复制品。我们可以投票重新打开,然后投票将其作为副本再次关闭。这似乎不值得付出努力。
- 相关:为什么是!/usr/bin/env bash优于!BI/BASH?在这样
- 我很震惊。令人震惊的是,Linux文档中充斥着重言式。xkcd.com/703 git-man-page-generator.lokaltog.net网站
- 一个主要区别是,根据系统的不同,#!/usr/bin/bash无法工作。在我的系统(Ubuntu 17.04)上,bash是/bin/bash,没有/usr/bin/bash。/bin和/usr/bin之间的区别很大程度上是任意的。另外,大多数系统把env命令放在/usr/bin中,但这并不能保证。
通过/usr/bin/env运行命令有助于查找当前环境中程序的默认版本。
这样,您就不必在系统上的特定位置查找它,因为这些路径可能位于不同系统上的不同位置。只要它在你的道路上,它就会找到它。
一个缺点是,如果您希望支持Linux,您将无法传递多个参数(例如,您将无法写入/usr/bin/env awk -f),因为posix对行的解释方式很模糊,Linux解释第一个空格后的所有内容以表示单个参数。您可以在某些版本的env上使用/usr/bin/env -S来解决这个问题,但是脚本将变得更不可移植,并在相当新的系统上中断(例如,如果不是更晚的话,甚至Ubuntu 16.04)。
另一个缺点是,由于您没有调用显式可执行文件,因此它有可能出错,并且在多用户系统安全问题上(例如,如果有人设法在您的路径中获得其可执行文件bash)。
1 2
| #!/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将选项传递给底层解释器!
- @戈拉瓦加瓦尔:不在我的系统上。只包含这一行的脚本:#!/usr/bin/env echo Hello抱怨:/usr/bin/env: echo Hello: No such file or directory。显然,它将echo Hello视为/usr/bin/env的单一论据。
- @基思汤普森:你能提供更多关于你的系统的信息吗?
- @keitththompson是env的gnu coreutils版本,它允许将args传递给命令。freebsd env似乎也支持它。你在用什么?:)
- @andr&;laszlo:env命令当然允许将参数传递给该命令。问题在于#!行的语义,这取决于内核。最近的Linux内核确实允许像#!/usr/bin/env command args这样的操作,但是旧的Linux内核和其他系统不允许。
- 是的,我刚测试过。抱歉,应该仔细阅读您的评论:)stackoverflow.com/questions/3306518/…
- 为什么代码块中有反勾号?他们不应该被移走吗?
- 使命令更加突出?说实话,那是四年前的事了,所以我没有正当的理由。在shell脚本中,您是正确的,它们不会出现在那里,而且不可否认的是,当时我没有那么多地深入研究SE标记。
- 注意:在大多数系统中,有一个假设env应该在/usr/bin/env中。在许多情况下,bash是在/bin/bash中的,所以使用/usr/bin/env可能只是一个小的灵活性…另一个注意事项是在一些(旧的?)系统/usr是一个不同的分区,不能安装,可能是因为这个原因!/通常使用垃圾桶/垃圾桶,而不是!/UR/BIN/BASH
使用#!/usr/bin/env NAME可以让shell搜索$path环境变量中名称的第一个匹配项。如果您不知道绝对路径或不想搜索它,它可能很有用。
- 至少你要知道env在哪里。
- 回答得很好。简洁地解释env shebang所做的,而不是说"根据系统配置选择程序"。
通过使用env命令,没有像/usr/bin/bash/中那样显式定义解释器的路径,而是从第一次找到解释器的地方搜索并启动解释器。这既有好处也有坏处
- 在大多数系统上,它们的功能是相同的,但这取决于bash和env可执行文件的位置。但不确定这将如何影响环境变量。
- "通过向解释器提供完整的路径,可以在不使用env的情况下指定解释器。一个问题是,在不同的计算机系统上,确切的路径可能不同。相反,使用env,解释器在脚本运行时被搜索并定位。这使得脚本更易于移植,但也增加了选择错误解释器的风险,因为它在可执行搜索路径上的每个目录中搜索匹配项。它也面临同样的问题,即到env二进制文件的路径在每台机器的基础上也可能不同。
我觉得它很有用,因为当我不知道env的时候,在我开始写脚本之前,我就这样做了:
1
| type nodejs > scriptname.js #or any other environment |
然后我把文件中的那一行修改为shebang。我这样做是因为我不记得nodejs在我的电脑上的位置--/usr/bin/或/bin/,所以对我来说env非常有用。也许有细节,但这是我的原因
如果壳牌剧本以#!/bin/bash开始,它们将永远与bash一起运行。如果他们仍然以#!/usr/bin/env bash开始,他们将在$PATH开始寻找,然后第一个可以找到。
为什么这会很有用?如果你想运行的脚本,需要Bash 4.X或Newer,你的系统只有bash〔1〕3.X安装,目前的分布并不提供一个新的版本,或者你是一个管理者,无法改变系统上安装的内容。
在课程中,您可以下载源代码,并从脚本中建立自己的基础,以~/bin为例。你也可以修改你的$PATH变量,1〔10〕File to include ~/bin作为第一个入口(PATH=$HOME/bin:$PATH作为EDOCX1〕〔13〕不会扩展到$PATH。如果你现在叫bash,壳牌将首先在$PATH上看着它,所以它将以~/bin开始,它将在哪里找到你的bash。同样的情况,如果用#!/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.
有一些系统只允许一个论据,所以你不能这样做,因为系统会看到EDOCX1[…]24],作为一个论据(他们会把它当作表达式的一部分)和Thus EDOCX1[…]22],将搜索一个翻译名称EDOCX1[…]24]。注意到这不是envCommand ITSELF的问题,ITSELF always allowed multiple parameters to be passed through,but with the Shebang Parser of the system that Parses this line before even calling env意思是,当这个已经固定在最基本的系统上,但如果你的脚本想要超便携,你不能认为这个已经固定在系统上,你会跑步。
它甚至可能产生安全影响,E.G.If sudowas not figured to clean environment or $PATHwas excluded from clean up.让我看看
通常,这是一个很好的保护场所,只有它能改变任何东西。你的家庭目录不是,尽管,你运行的任何程序都可以改变它。这意味着聪明的代码可以将一个伪造的bash〔1〕转换成隐藏的目录,修改.bash_profile包括在你的$PATH中的这个目录,所以所有的脚本都会使用#!/usr/bin/env bash结束与这个伪造的EDOCX1〕一起运行。如果你有麻烦的话,请继续。
E.G.Consider a tool creates a file ~/.evil/bashwith the following content:
1 2 3 4 5 6 7 8
| #!/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
ZZU1
Proof of concept(on a system where EDOCX1&29).Keeps EDOCX1&5):
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| $ ./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中,如果你不想把它们放在那里,无论原因为何,这并不是一个真正的问题,也不是一个将SYMLINK放在/bin的问题。如果他们不工作,那就太多了。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 a t all)but they're so common,even if a system would not offer a /bin/sh,it could still understand #!/bin/shand know what to do with it and may be conversability for itITH Existing Code.
但对于更现代的、非标准的、选择性的解释者,比如珍珠、PHP、Python或红宝石,这并不是真的具体说明他们应在何处定位。他们可能在/usr/bin但他们可能在/usr/local/bin或完全不同的等级分支机构(/opt/...,/Applications/...等)。这就是为什么他们经常使用#!/usr/bin/env xxxShebang Syntax。