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

icomxhvb  于 2022-12-11  发布在  Linux
关注(0)|答案(6)|浏览(158)

在Bash脚本的头中,这两条语句有什么区别:

  1. #!/usr/bin/env bash
  2. #!/usr/bin/bash
    当我查阅envman page时,我得到这样的定义:
env - run a program in a modified environment

这是什么意思?

kmbjn2e3

kmbjn2e31#

通过/usr/bin/env运行一个命令的好处是可以在当前的环境中查找程序的默认版本。
这样,您就不必在系统的特定位置查找它,因为这些路径可能在不同系统的不同位置。只要它在您的路径中,它就会找到它。
一个缺点是不能传递多个参数(例如,您将无法编写/usr/bin/env awk -f),如果您希望支持Linux,则将该行解释为POSIX is vague,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脚本,而不必重新编写可执行代码),但在安全性是重点的情况下,后者可能是首选的,因为它限制了代码注入的可能性。

9lowa7mx

9lowa7mx2#

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

nfg76nw0

nfg76nw03#

如果shell脚本以#!/bin/bash开头,它们将始终从/bin开始以bash运行。但是,如果它们以#!/usr/bin/env bash开头,它们将在$PATH中搜索bash,然后从它们能找到的第一个开始。
假设您要运行bash脚本,需要bash 4.x或更高版本,而您的系统只安装了bash 3.x,并且当前您的发行版没有提供更新的版本,或者您不是管理员,无法更改系统上安装的内容。
当然,您可以下载bash源代码并从头构建自己的bash,例如将其放置到~/bin中。(PATH=$HOME/bin:$PATH作为~不会在$PATH中展开).如果您现在调用bash,shell将首先在$PATH中按顺序查找它,因此它以~/bin开头,如果脚本使用#!/usr/bin/env bash搜索bash,也会发生同样的事情,所以这些脚本现在可以使用您的自定义bash构建在您的系统上工作。
一个缺点是,这可能会导致意外的行为,例如,同一台机器上的同一脚本可能会使用不同的解释器运行,以用于不同的环境或具有不同搜索路径的用户,从而导致各种各样的麻烦。
env最大的缺点是,某些系统只允许一个参数,因此您不能这样做#!/usr/bin/env <interpreter> <arg>,因为系统将<interpreter> <arg>视为一个自变量(它们将把它当作引用了该表达式),因此env将搜索名为<interpreter> <arg>的解释器。请注意,这不是env命令本身的问题,它总是允许传递多个参数,但是系统的shebang解析器甚至在调用env之前就解析这一行。同时,这个问题已经在大多数系统上得到了修复,但是如果你的脚本想要超便携,你不能相信这个问题已经在你将要运行的系统上得到了修复。
它甚至可能会带来安全隐患,例如,如果sudo未配置为清理环境,或者$PATH被排除在清理之外。
通常/bin是一个受到很好保护的地方,只有root可以在那里修改任何东西。但是你的home目录不是,你运行的任何程序都可以对它进行修改。这意味着恶意代码可能会把一个假的bash放在某个隐藏的目录中,修改你的.bash_profile以将该目录包含在你的$PATH中,因此所有使用#!/usr/bin/env bash的脚本最终都将使用伪bash运行。
例如,假设一个工具创建了一个包含以下内容的文件~/.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

通常,经典shell都应该位于/bin中,如果出于任何原因不想将它们放在那里,那么在/bin中放置一个指向它们实际位置的符号链接确实不是问题(或者可能/bin本身是符号链接),所以我总是使用#!/bin/sh#!/bin/bash。如果它们不再工作,那么会有太多的东西被破坏。POSIX并不需要这些位置(POSIX没有标准化路径名,因此它甚至根本没有标准化shebang特性),但它们是如此普遍,即使一个系统不提供/bin/sh,它可能仍然理解#!/bin/sh,并知道如何使用它,可能只是为了与现有代码兼容。
但是对于更现代的、非标准的、可选的解释器,如Perl、PHP、Python或Ruby,并没有真正指定它们应该位于哪里。(/opt/.../Applications/...,等等)。这就是为什么它们经常使用#!/usr/bin/env xxx shebang语法的原因。

s4chpxco

s4chpxco4#

/usr/bin/bash/中显式定义解释器的路径不同,通过使用env命令,可以从第一次找到解释器的位置搜索并启动解释器。

jw5wzhpr

jw5wzhpr5#

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

type nodejs > scriptname.js #or any other environment

然后我把文件中的那一行修改成了shebang。
我之所以这样做,是因为我不总是记得nodejs在我的计算机上的位置-- /usr/bin/或/bin/,所以对我来说env非常有用。

piztneat

piztneat6#

一个原因是它可以在Linux和BSD上移植。

相关问题