/ usr / bin / env关于shebang行的问题

问题

  • 如果你把一个shell脚本粘贴到shebang行,内核是做什么的?
  • 内核如何知道启动哪个解释器?

说明

我最近想写一个/ usr / bin / env的包装器,因为我的CGI环境不允许我设置PATHvariables,除了全局的(当然很糟糕!)。

所以我想,“好吧,让我们设置PREPENDPATH并设置围绕env的包装中的PATH ”。 由此产生的脚本(这里称为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的源代码之后,我仍然迷失了方向。 它设置环境并启动程序(或者对我来说似乎…)。

Solutions Collecting From Web of "/ usr / bin / env关于shebang行的问题"

首先,你应该很少使用$* ,你应该几乎总是使用"$@"来代替。 这里有很多问题可以解释为什么。

其次 – env命令有两个主要用途。 一个是打印当前的环境; 另一种是在运行时完全控制命令的环境。 你正在演示的第三个用途是修改环境,但坦率地说,没有必要这样 – 炮弹能够为你处理。

模式1:

 env 

模式2:

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

该版本取消了所有继承的环境变量,并运行了与ENVVAR = value选项设置的环境完全相同的command

第三种模式 – 修改环境 – 不那么重要,因为你可以用普通的(文明的)炮弹来做到这一点。 (这意味着“不是C shell” – 同样,还有其他的问题可以解释这个答案。)例如,你可以做得很好:

 #!/bin/bash export PATH=${PREPENDPATH:?}:$PATH exec python "$@" 

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

 #!/bin/bash PATH=${PREPENDPATH:?}:$PATH exec python "$@" 

从表面上看,这是一样的。 但是,这将执行在预先存在的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 "$@" case "$*" in (*v*) track-oninit-v $stdout;; esac 

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

当你的系统看到shebang对应的幻数时,它会在shebang之后的给定路径上执行一个execve ,并将脚本本身作为参数。

您的脚本失败,因为您提供的文件( /usr/bin/env.1 )不是可执行文件 ,而是由shebang开始。

理想情况下,你可以解决它… env脚本作为一个shebang这行:

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

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

所以我看到的唯一方法是用C写你的env.1

编辑:似乎没有人相信我^^,所以我写了一个简单而肮脏的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; }