在Bash脚本的标头中,这两个语句之间有什么区别:
#!/usr/bin/env bash
#!/usr/bin/bash
当查阅env
手册页时,得到以下定义:
env - run a program in a modified environment
这是什么意思?
在Bash脚本的标头中,这两个语句之间有什么区别:
#!/usr/bin/env bash
#!/usr/bin/bash
当查阅env
手册页时,得到以下定义:
env - run a program in a modified environment
这是什么意思?
Answers:
运行命令,通过/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脚本,而不必重新编写可执行代码行)。但是在以安全性为重点的情况下,后者是首选,因为它限制了代码注入的可能性。
#!/usr/bin/env echo Hello
抱怨:/usr/bin/env: echo Hello: No such file or directory
。显然,它被视为echo Hello
的单个参数/usr/bin/env
。
env
命令当然允许将参数传递给该命令。问题是该#!
行的语义,并且取决于内核。最近的Linux内核确实允许类似的东西#!/usr/bin/env command args
,但是较旧的Linux内核和其他系统则不允许。
使用#!/usr/bin/env NAME
使外壳程序在$ PATH环境变量中搜索NAME的第一个匹配项。如果您不知道绝对路径或不想搜索绝对路径,它可能会很有用。
无需/usr/bin/bash/
使用env命令像中那样显式定义解释器的路径,而是从首次找到解释器的位置搜索并启动解释器。这既有好处也有缺点
如果shell脚本以开头#!/bin/bash
,它们将始终以bash
from 开头/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 xxx
shebang语法的原因。