7

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?

Explanation:

I recently wanted to write a wrapper around /usr/bin/env because my CGI Environment does not allow me to set the PATH variable, except globally (which of course sucks!).

So I thought, "OK. Let's set PREPENDPATH and set PATH in a wrapper around env.". The resulting script (here called env.1) looked like this:

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

which looks like it should work. I checked how they both react, after setting 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!".

# 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.

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...).

4

2 に答える 2

6

まず第一に、使用することはめったになく$*、ほとんどの場合、代わりに使用する必要があります"$@"。SOには、その理由の詳細を説明する多くの質問があります。

2番目-envコマンドには2つの主な用途があります。1つは、現在の環境を印刷することです。もう1つは、コマンドの実行時にコマンドの環境を完全に制御することです。あなたが示している3番目の使用法は、環境を変更することですが、率直に言って、その必要はありません-シェルはあなたのためにそれを処理することができます。

モード1:

env

モード2:

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

このバージョンは、継承されたすべての環境変数をキャンセルcommandし、ENVVAR=valueオプションで設定された環境で正確に実行されます。

3番目のモード(環境の修正)は、通常の(文明化された)シェルでうまく実行できるため、それほど重要ではありません。(これは「Cシェルではない」という意味です。これも、SOに関する他の質問と、それを説明する回答があります。)たとえば、次のように完全に実行できます。

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

これは$PREPENDPATH、環境内で空でない文字列に設定されていることを要求し、それをの前に$PATH追加して、新しいPATH設定をエクスポートします。次に、その新しいPATHを使用してpython、関連する引数を使用してプログラムを実行します。はexec、シェルスクリプトを。に置き換えますpython。これは次のものとはまったく異なることに注意してください。

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

表面的には、これは同じです。ただし、これpythonにより、プロセスの環境でPATHの新しい値が使用されていても、既存の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
于 2008-12-11T06:01:53.590 に答える
4

shebangに関するウィキペディアの記事を注意深く読む必要があります。

システムは、シバンに対応するマジック ナンバーを検出するとexecve、指定されたパスでシバンの後に を実行し、スクリプト自体を引数として指定します。

あなたが与えるファイル ( /usr/bin/env.1) は実行可能ファイルではなく、シバンで始まるため、スクリプトは失敗します....

env理想的には、次の行をシバンとして使用してスクリプトで...を使用して解決できます。

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

「」をパスとして扱うため、Linuxでは/usr/bin/env.1 python機能しません(引数を分割しません)

だから私が見る唯一の方法はあなたenv.1をCで書くことです

編集: 誰も私を信じていないようです ^^, だから私はシンプルで汚いを書きました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;
}
于 2008-12-09T14:07:48.353 に答える