2008-12-09 49 views
6

问题在/ usr/bin中/ env的问题:关于家当线pecularities

  • 是什么内核做,如果你坚持一个shell脚本到家当线?
  • Kernel如何知道启动哪个解释器?

说明

我最近想写一个包装围绕在/ usr/bin中/ env的因为我的CGI环境不允许我设置全局PATH变量,但(这当然很烂!)。

所以我想, “OK,让我们设置PREPENDPATH并设置PATH围绕ENV的包装。”得到的脚本(这里称为env.1)是这样的:

#!/bin/bash 
/usr/bin/env PATH=$PREPENDPATH:$PATH $* 

它看起来像它应该工作。我查了一下他们俩的反应如何,设置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 

看起来绝对完美!到现在为止还挺好。但看看“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!" 

我想我失去了一些东西有关UNIX非常基本的。

即使在查看原始env的源代码后,我仍然迷失了方向。它设置了环境并启动了程序(或者对我而言似乎......)。

回答

6

首先,你应该很少使用$*,你应该几乎总是用"[email protected]"来代替。这里有很多关于SO的问题可以解释为什么。

其次 - env命令有两个主要用途。一个是打印当前的环境;另一个是在运行时完全控制命令的环境。你正在演示的第三种用途是修改环境,但坦率地说,没有必要这么做 - shell很适合你。

模式1:

env 

模式2:

env -i HOME=$HOME PATH=$PREPENDPATH:$PATH ... command args 

该版本将取消所有继承的环境变量,并运行command与精确的在ENVVAR = value选项设置环境。

第三种模式 - 修改环境 - 不那么重要,因为你可以使用常规(文明)外壳来做到这一点。 (这意味着“不是C壳” - 再次,有上,这样解释说,其他问题的答案)。例如,你可以非常清楚这样做:

#!/bin/bash 
export PATH=${PREPENDPATH:?}:$PATH 
exec python "[email protected]" 

这坚称$PREPENDPATH被设置为一个非在环境中为空字符串,然后将其前置到$PATH,并导出新的PATH设置。然后,使用该新PATH,它将执行带有相关参数的python程序。 execpython替换shell脚本。请注意,这是相当不同的:

#!/bin/bash 
PATH=${PREPENDPATH:?}:$PATH exec python "[email protected]" 

表面上,这是相同的。但是,这将执行在预先存在的PATH上找到的python,尽管在过程环境中具有新的PATH值。因此,在这个例子中,你最终会从/usr/bin执行Python,而不是从/home/pi/prepend/bin执行。

在你的情况下,我可能不会使用env,只会使用显式导出的脚本的适当变体。

env命令是不常见的,因为它不能识别双击连接符以将选项与命令的其余部分分开。这部分是因为它不需要很多选项,部分原因是ENVVAR = value选项应该在双短划线之前还是之后出现并不清楚。

我实际上有一系列用于运行(不同版本)数据库服务器的脚本。这些脚本真正使用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 "$0: 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 "[email protected]" 

case "$*" in 
(*v*) track-oninit-v $stdout;; 
esac 
+0

这是有用的信息,但是作为OP说,他/她的内核将无法运行非二进制文件(比如你提供的bash存根)作为shebang行的第一个元素。我的也不会;具体来说,内核默默无法运行脚本,并在所需的(非二进制)解释器中运行有问题的shebang行,然后我的shell会尝试运行脚本。 (我的理解是,这是一个很多shell保留的旧时shell行为。) – dubiousjim 2012-04-23 17:41:35

4

您应该仔细阅读维基百科有关shebang的文章。

当您的系统看到shebang对应的幻数时,它在shebang后的给定路径上执行execve,并将该脚本本身作为参数。因为你给的文件(/usr/bin/env.1)不是可执行,而是由认领开始自己理想的

你的脚本失败....

,你可以用它解决...env与此线作为认领你的脚本:

#!/usr/bin/env /usr/bin/env.1 python 

它虽然不会在Linux上,因为它将“/usr/bin/env.1 python”为路径工作(它不分裂参数)

所以,唯一的路上我看到的是写你用C

env.1编辑:好像没有人belives我^^,所以我写了一个简单而肮脏的env.1.c

#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; 
}