在bash
脚本的标题中,这两个语句之间的区别是什么?
#!/usr/bin/env bash
#!/usr/bin/bash
当我试图查看env
手册页时,我得到了这个定义:
env - run a program in a modified environment
这是什么意思?
答案 0 :(得分:263)
通过/usr/bin/env
运行命令的好处是可以查找当前 env 项目中默认版本的程序。
这样,您不必在系统上的特定位置查找它,因为这些路径可能位于不同系统上的不同位置。只要它在你的道路上,它就会找到它。
一个缺点是,如果你想支持Linux,你将无法传递多个参数(例如,你将无法编写/usr/bin/env awk -f
),POSIX is vague关于该行是如何解释,并且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脚本,而不必重写可执行行)。但在以安全性为重点的情况下,后者将是首选,因为它限制了代码注入的可能性。
答案 1 :(得分:46)
使用#!/usr/bin/env NAME
使shell在$ PATH环境变量中搜索NAME的第一个匹配项。如果您不知道绝对路径或不想搜索它,它会很有用。
答案 2 :(得分:11)
通过使用env命令,而不是像/usr/bin/bash/
中那样明确定义解释器的路径,而是从首次找到解释器的任何地方搜索并启动解释器。这有upsides and downsides
答案 3 :(得分:3)
我发现它很有用,因为当我不知道env时,在我开始编写脚本之前我就是这样做的:
type nodejs > scriptname.js #or any other environment
然后我将文件中的那一行修改为shebang
我这样做,因为我并不总是记得我的计算机上的nodejs在哪里 - / usr / bin /或/ bin /,所以对我来说env
非常有用。也许这里有细节,但这是我的理由
答案 4 :(得分:2)
如果外壳程序脚本以#!/bin/bash
开头,则它们将始终以bash
中的/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
,因为~
不会在{{1 }})。如果现在调用$PATH
,则外壳程序将首先在bash
中按顺序查找它,因此它以$PATH
开头,它将在其中找到您的~/bin
。如果脚本使用bash
搜索bash
,也会发生同样的事情,因此这些脚本现在可以使用自定义的#!/usr/bin/env bash
构建在您的系统上运行。
缺点是,这可能导致意外行为,例如同一台计算机上的同一脚本可能针对不同的环境或具有不同搜索路径的用户使用不同的解释器运行,从而引起各种麻烦。
bash
的最大缺点是某些系统仅允许一个参数,因此您无法执行env
,因为系统会将#!/usr/bin/env <interpreter> <arg>
视为一个参数(它们将视为就像表达式被引用一样),因此<interpreter> <arg>
将搜索名为env
的解释器。请注意,这不是<interpreter> <arg>
命令本身的问题,它始终允许传递多个参数,但是使用系统的shebang解析器来解析此行,甚至在调用env
之前也是如此。同时,此问题已在大多数系统上得到解决,但是如果您的脚本要具有超强的可移植性,则不能依靠此脚本已在要运行的系统上得到解决。
它甚至可能带来安全隐患,例如如果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
。如果这些都不起作用了,那么太多的东西都会崩溃。并不是说POSIX会要求这些位置(POSIX并没有标准化路径名,因此甚至根本没有标准化shebang功能),但是它们是如此普遍,以至于即使系统不提供#!/bin/bash
,它可能仍然会理解/bin/sh
并知道如何处理它,并且可能仅仅是为了与现有代码兼容。
但是对于更现代的,非标准的,可选的解释器(如Perl,PHP,Python或Ruby),并没有在应将其定位的任何地方指定它。它们可能在#!/bin/sh
中,但也可能在/usr/bin
中或完全不同的层次结构分支中(/usr/local/bin
,/opt/...
等)。这就是为什么它们经常使用/Applications/...
shebang语法的原因。