问题:
如果你将shell脚本粘贴到shebang行中,内核会做什么?
内核如何知道要启动哪个解释器?
说明:
我最近想在/ usr/bin/env周围编写一个包装器,因为我的CGI环境不允许我设置PATH变量,除了全局(当然这很糟糕!).
所以我想,"好的.让我们设置PREPENDPATH并在环绕包装器中设置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.它设置环境并启动程序(或者在我看来......).
首先,你应该很少使用$*
,你应该几乎总是使用"$@"
.这里有很多关于SO的问题,这些问题解释了原因的来龙去脉.
第二 - 该env
命令有两个主要用途.一是打印当前环境; 另一种是在命令运行时完全控制命令的环境.您正在演示的第三个用途是修改环境,但坦率地说没有必要 - 外壳完全能够为您处理.
模式1:
env
模式2:
env -i HOME=$HOME PATH=$PREPENDPATH:$PATH ... command args
此版本取消所有继承的环境变量,并command
使用ENVVAR = value选项设置的环境运行.
第三种模式 - 修改环境 - 不太重要,因为你可以用常规(文明)炮弹做到这一点.(这意味着"不是C shell" - 再次,在SO上还有其他问题,答案可以解释这一点.)例如,你可以做得非常好:
#!/bin/bash export PATH=${PREPENDPATH:?}:$PATH exec python "$@"
这坚持$PREPENDPATH
在环境中设置为非空字符串,然后将其预先添加$PATH
,并导出新的PATH设置.然后,使用该新PATH,它python
使用相关参数执行程序.该exec
替换的shell脚本python
.请注意,这与以下内容完全不同:
#!/bin/bash PATH=${PREPENDPATH:?}:$PATH exec python "$@"
从表面上看,这是一样的.但是,这将python
在预先存在的PATH上执行找到,尽管在进程环境中使用PATH的新值.因此,在示例中,您最终将/usr/bin
从而不是从中执行Python /home/pi/prepend/bin
.
在您的情况下,我可能不会使用env
,并且只会使用显式导出的脚本的适当变体.
该env
命令不常见,因为它无法识别双破折号以将选项与命令的其余部分分开.这部分是因为它不需要很多选项,部分原因是尚不清楚ENVVAR =值选项是应该在双破折号之前还是之后.
我实际上有一系列用于运行(不同版本)数据库服务器的脚本.这些脚本确实使用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