apache /usr/bin/env 关于shebang 线特性的问题
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/352532/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
/usr/bin/env questions regarding shebang line pecularities
提问by pi.
Questions:
问题:
- What does the kernel do if you stick a shell-script into the shebang line?
- How does the Kernel know which interpreter to launch?
- 如果将 shell 脚本插入 shebang 行,内核会做什么?
- 内核如何知道要启动哪个解释器?
Explanation:
说明:
I recently wanted to write a wrapper around /usr/bin/envbecause my CGI Environment does not allow me to set the PATHvariable, except globally (which of course sucks!).
我最近想围绕/usr/bin/env编写一个包装器,因为我的 CGI 环境不允许我设置PATH变量,除了全局变量(这当然很糟糕!)。
So I thought, "OK. Let's set PREPENDPATH and set PATHin a wrapper around env.". The resulting script (here called env.1) looked like this:
所以我想,“好吧。让我们设置 PREPENDPATH 并在 env 的包装器中设置PATH。”。生成的脚本(此处称为env.1)如下所示:
#!/bin/bash
/usr/bin/env PATH=$PREPENDPATH:$PATH $*
which looks like it should work. I checked how they both react, after setting PREPENDPATH:
看起来它应该工作。在设置 PREPENDPATH 后,我检查了它们的反应:
$ which /usr/bin/env python
/usr/bin/env
/usr/bin/python
$ which /usr/bin/env.1 python
/usr/bin/env
/home/pi/prepend/bin/python
Look absolutely perfect! So far, so good. But look what happens to "Hello World!".
看起来绝对完美!到现在为止还挺好。但是看看“Hello World!”会发生什么。
# Shebang is #!/usr/bin/env python
$ test-env.py
Hello World!
# Shebang is #!/usr/bin/env.1 python
$ test-env.1.py
Warning: unknown mime-type for "Hello World!" -- using "application/*"
Error: no such file "Hello World!"
I guess I am missing something pretty fundamental about UNIX.
我想我遗漏了一些关于 UNIX 的基本知识。
I'm pretty lost, even after looking at the source code of the original env. It sets the environment and launches the program (or so it seems to me...).
即使查看了原始env的源代码,我也很迷茫。它设置环境并启动程序(或者在我看来是这样......)。
采纳答案by Jonathan Leffler
First of all, you should very seldom use $*and you should almost always use "$@"instead. There are a number of questions here on SO which explain the ins and outs of why.
首先,你应该很少使用$*,你应该几乎总是使用"$@"。这里有很多关于 SO 的问题,它们解释了原因的来龙去脉。
Second - the envcommand has two main uses. One is to print the current environment; the other is to completely control the environment of a command when it is run. The third use, which you are demonstrating, is to modify the environment, but frankly there's no need for that - the shells are quite capable of handling that for you.
其次 - 该env命令有两个主要用途。一是打印当前环境;另一种是完全控制一个命令运行时的环境。您正在演示的第三种用途是修改环境,但坦率地说,没有必要这样做 - 外壳非常有能力为您处理。
Mode 1:
模式一:
env
Mode 2:
模式二:
env -i HOME=$HOME PATH=$PREPENDPATH:$PATH ... command args
This version cancels all inherited environment variables and runs commandwith precisely the environment set by the ENVVAR=value options.
此版本取消了所有继承的环境变量,并command在 ENVVAR=value 选项设置的精确环境下运行。
The third mode - amending the environment - is less important because you can do that fine with regular (civilized) shells. (That means "not C shell" - again, there are other questions on SO with answers that explain that.) For example, you could perfectly well do:
第三种模式 - 修改环境 - 不太重要,因为您可以使用常规(文明)shell 做到这一点。(这意味着“不是 C shell”——同样,还有其他关于 SO 的问题的答案可以解释这一点。)例如,你完全可以这样做:
#!/bin/bash
export PATH=${PREPENDPATH:?}:$PATH
exec python "$@"
This insists that $PREPENDPATHis set to a non-empty string in the environment, and then prepends it to $PATH, and exports the new PATH setting. Then, using that new PATH, it executes the pythonprogram with the relevant arguments. The execreplaces the shell script with python. Note that this is quite different from:
这坚持将$PREPENDPATH设置为环境中的非空字符串,然后将其添加到$PATH,并导出新的 PATH 设置。然后,使用新的 PATH,它python使用相关参数执行程序。该exec替换的shell脚本python。请注意,这与以下内容完全不同:
#!/bin/bash
PATH=${PREPENDPATH:?}:$PATH exec python "$@"
Superficially, this is the same. However, this will execute the pythonfound on the pre-existing PATH, albeit with the new value of PATH in the process's environment. So, in the example, you'd end up executing Python from /usr/binand not the one from /home/pi/prepend/bin.
从表面上看,这是一样的。但是,这将执行在python预先存在的 PATH 上找到的,尽管在进程环境中使用新的 PATH 值。所以,在这个例子中,你最终会执行 Python from/usr/bin而不是 from 的那个/home/pi/prepend/bin。
In your situation, I would probably not use envand would just use an appropriate variant of the script with the explicit export.
在您的情况下,我可能不会使用env,而只会使用带有显式导出的脚本的适当变体。
The envcommand is unusual because it does not recognize the double-dash to separate options from the rest of the command. This is in part because it does not take many options, and in part because it is not clear whether the ENVVAR=value options should come before or after the double dash.
该env命令很不寻常,因为它无法识别双破折号来将选项与命令的其余部分分开。这部分是因为它没有很多选项,部分是因为不清楚 ENVVAR=value 选项应该在双破折号之前还是之后。
I actually have a series of scripts for running (different versions of) a database server. These scripts really use env(and a bunch of home-grown programs) to control the environment of the server:
我实际上有一系列用于运行(不同版本)数据库服务器的脚本。这些脚本确实使用env(和一堆本土程序)来控制服务器的环境:
#!/bin/ksh
#
# @(#)$Id: boot.black_19.sh,v 1.3 2008/06/25 15:44:44 jleffler Exp $
#
# Boot server black_19 - IDS 11.50.FC1
IXD=/usr/informix/11.50.FC1
IXS=black_19
cd $IXD || exit 1
IXF=$IXD/do.not.start.$IXS
if [ -f $IXF ]
then
echo "#!/usr/bin/env /usr/bin/env.1 python
: will not start server $IXS because file $IXF exists" 1>&2
exit 1
fi
ONINIT=$IXD/bin/oninit.$IXS
if [ ! -f $ONINIT ]
then ONINIT=$IXD/bin/oninit
fi
tmpdir=$IXD/tmp
DAEMONIZE=/work1/jleffler/bin/daemonize
stdout=$tmpdir/$IXS.stdout
stderr=$tmpdir/$IXS.stderr
if [ ! -d $tmpdir ]
then asroot -u informix -g informix -C -- mkdir -p $tmpdir
fi
# Specialized programs carried to extremes:
# * asroot sets UID and GID values and then executes
# * env, which sets the environment precisely and then executes
# * daemonize, which makes the process into a daemon and then executes
# * oninit, which is what we really wanted to run in the first place!
# NB: daemonize defaults stdin to /dev/null and could set umask but
# oninit dinks with it all the time so there is no real point.
# NB: daemonize should not be necessary, but oninit doesn't close its
# controlling terminal and therefore causes cron-jobs that restart
# it to hang, and interactive shells that started it to hang, and
# tracing programs.
# ??? Anyone want to integrate truss into this sequence?
asroot -u informix -g informix -C -a dbaao -a dbsso -- \
env -i HOME=$IXD \
INFORMIXDIR=$IXD \
INFORMIXSERVER=$IXS \
INFORMIXCONCSMCFG=$IXD/etc/concsm.$IXS \
IFX_LISTEN_TIMEOUT=3 \
ONCONFIG=onconfig.$IXS \
PATH=/usr/bin:$IXD/bin \
SHELL=/usr/bin/ksh \
TZ=UTC0 \
$DAEMONIZE -act -d $IXD -o $stdout -e $stderr -- \
$ONINIT "$@"
case "$*" in
(*v*) track-oninit-v $stdout;;
esac
回答by Piotr Lesnicki
You should carefully read the wikipedia article about shebang.
您应该仔细阅读维基百科关于shebang 的文章。
When your system sees the magic number corresponding to the shebang, it does an execveon the given path after the shebang and gives the script itself as an argument.
当您的系统看到与 shebang 对应的幻数时,它会execve在 shebang 之后的给定路径上执行 an ,并将脚本本身作为参数。
Your script fails because the file you give (/usr/bin/env.1) is not an executable, but begins itself by a shebang....
您的脚本失败,因为您提供的文件 ( /usr/bin/env.1) 不是一个可执行文件,而是以一个shebang 开头....
Ideally, you could resolve it using... envon your script with this line as a shebang:
理想情况下,您可以使用...env在您的脚本中使用这一行作为shebang来解决它:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
const char* prependpath = "/your/prepend/path/here:";
int main(int argc, char** argv){
int args_len = argc + 1;
char* args[args_len];
const char* env = "/usr/bin/env";
int i;
/* arguments: the same */
args[0] = env;
for(i=1; i<argc; i++)
args[i] = argv[i];
args[argc] = NULL;
/* environment */
char* p = getenv("PATH");
char* newpath = (char*) malloc(strlen(p)
+ strlen(prependpath));
sprintf(newpath, "%s%s", prependpath, p);
setenv("PATH", newpath, 1);
execv(env, args);
return 0;
}
It won't work though on linux as it treats "/usr/bin/env.1 python" as a path (it doesn't split arguments)
它在 linux 上不起作用,因为它将“ /usr/bin/env.1 python”视为路径(它不拆分参数)
So the only way I see is to write your env.1in C
所以我看到的唯一方法是env.1用 C编写你的
EDIT: seems like no one belives me ^^, so I've written a simple and dirty env.1.c:
编辑:似乎没有人相信我 ^^,所以我写了一个简单而肮脏的env.1.c:

