2008-12-09 2 views
6

Fragen:/usr/bin/env Fragen zu shebang Linie Eigentümlichkeiten

  • Was tut der Kernel tun, wenn Sie ein Shell-Skript in die shebang Linie kleben?
  • Woher weiß der Kernel, welcher Interpreter gestartet werden soll?

Erklärung:

Ich wollte vor kurzem einen Wrapper schreiben um /usr/bin/env weil mein CGI-Environment mich nicht zulässt, dass die PATH Variable setzen, mit der Ausnahme global (die natürlich saugt!).

Also dachte ich: "OK. Lassen Sie uns PREPENDPATH setzen und setzen PATH in einem Wrapper um env.". Das resultierende Skript (hier genannt env.1) sah wie folgt aus:

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

, die aussieht wie es funktionieren sollte. Ich habe, wie sie beide reagieren, nachdem PREPENDPATH Einstellung:

$ 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 

Schauen Sie absolut perfekt! So weit, ist es gut. Aber schau, was mit "Hello World!" Passiert.

# 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!" 

Ich denke, ich vermisse etwas sehr grundlegendes über UNIX.

Ich bin ziemlich verloren, auch nach dem Blick auf den Quellcode des ursprünglichen env. Es setzt die Umgebung und startet das Programm (oder so scheint es mir ...).

Antwort

6

Zunächst sollten Sie sehr selten $* verwenden und Sie sollten stattdessen fast immer "[email protected]" verwenden. Hier gibt es eine Reihe von Fragen zur SO, die das Warum erklären.

Zweitens - der env Befehl hat zwei Hauptverwendungen. Eine besteht darin, die aktuelle Umgebung zu drucken; der andere besteht darin, die Umgebung eines Befehls vollständig zu steuern, wenn er ausgeführt wird. Die dritte Verwendung, die Sie demonstrieren, besteht darin, die Umgebung zu modifizieren, aber ehrlich gesagt gibt es keine Notwendigkeit dafür - die Schalen sind durchaus in der Lage, das für Sie zu handhaben.

Mode 1:

env 

Mode 2:

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

Diese Version hebt alle geerbten Umgebungsvariablen und läuft command genau die Umgebung festgelegt durch die ENVVAR = Wert-Optionen.

Der dritte Modus - Ändern der Umgebung - ist weniger wichtig, weil Sie mit regulären (zivilisierten) Shells das tun können. (. Das bedeutet „nicht C-Shell“ - auch hier gibt es noch andere Fragen auf SO mit Antworten, die das erklärt) Zum Beispiel könnte man sehr gut tun:

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

Diese besteht darauf, dass $PREPENDPATH zu einem nicht gesetzt leere Zeichenfolge in der Umgebung, und dann vor $PATH, und exportiert die neue PATH-Einstellung. Mit diesem neuen PATH führt er dann das Programm python mit den relevanten Argumenten aus. Die exec ersetzt das Shell-Skript mit python. Beachten Sie, dass dies ganz anders ist:

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

Vordergründig ist dies nichts weiter. Dies führt jedoch den python aus, der auf dem bereits vorhandenen PATH gefunden wurde, allerdings mit dem neuen Wert von PATH in der Prozessumgebung. Im Beispiel würden Sie also Python von /usr/bin ausführen und nicht den von /home/pi/prepend/bin.

In Ihrer Situation würde ich wahrscheinlich env nicht verwenden und würde nur eine geeignete Variante des Skripts mit dem expliziten Export verwenden.

Der Befehl env ist ungewöhnlich, weil er den Doppelstrich nicht erkennt, um Optionen vom Rest des Befehls zu trennen. Dies liegt zum Teil daran, dass es nicht viele Optionen erfordert, und zum Teil, weil es nicht klar ist, ob die ENVVAR = Wertoptionen vor oder nach dem Doppelstrich stehen sollten.

Ich habe tatsächlich eine Reihe von Skripten zum Ausführen (verschiedene Versionen) eines Datenbankservers. Diese Skripte verwendet env wirklich (und eine Reihe von home-grown-Programmen), um die Umgebung des Servers zu steuern:

#!/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

Dies ist nützlich, Informationen, aber wie die OP sagt, sein/ihr Kern nicht nicht laufen Binärdateien (wie die von Ihnen bereitgestellten Bash-Stubs) als erstes Element einer Shebang-Zeile. Meines wird auch nicht; Insbesondere führt der Kernel das Skript mit der problematischen Shebang-Zeile im gewünschten (nicht binären) Interpreter nicht automatisch aus, und dann versucht meine Shell stattdessen, das Skript auszuführen. (Mein Versteppern ist, dass dies ein altes Shell-Verhalten ist, das viele Shells behalten.) – dubiousjim

+0

Ich verstehe nicht, was Sie sagen? Sind Sie der Meinung, dass es (noch) Shells oder Kernel gibt, die '#!/Bin/bash' oder' #!/Bin/ksh' nicht als erste Zeile eines Skripts behandeln? Ich behaupte nicht oder zeige nicht, dass du '#!/Some/script' als Shebang benutzen kannst. –

+0

Den ersten nicht beanspruchen. In der Frage des OP geht es jedoch um die zweite Frage: Er/sie fragte: "Was macht der Kernel, wenn man ein Shell-Skript in die Shebang-Zeile steckt?" und meldet dann Probleme, die von der Verwendung von env.1 als Shebang-Zeile herrühren, wobei env.1 ein Skript ist. (Zufällig suchte ich nach Diskussionen über die Grenzen der Shebang-Linien, die mich hierher brachten. Und ich fand, dass Ihr Beitrag nützliche Informationen hatte. Danke, dass Sie etwas beigetragen haben. Ich habe nur darauf hingewiesen, dass andere Leser vielleicht später kommen. Das, was Sie diskutieren, hilft nicht dabei, die Einschränkungen zu überwinden, auf die das OP gestoßen ist.) – dubiousjim

4

Sie sollten den wikipedia Artikel über shebang sorgfältig lesen.

Wenn Ihr System die magische Zahl, die dem Shebang entspricht, sieht, macht es einen execve auf dem angegebenen Pfad nach dem Shebang und gibt das Skript selbst als Argument an.

Ihr Skript schlägt fehl, da die Datei, die Sie geben (/usr/bin/env.1) nicht eine ausführbare, sondern fängt an, sich durch eine shebang ....

Idealerweise könnte man es mit lösen ...env auf Ihrem Skript mit dieser Linie als shebang:

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

Es wird allerdings nicht auf Linux arbeiten, wie es „/usr/bin/env.1 python“ als Weg behandelt So (es spielt keine Argumente geteilt)

die einzigen wie ich sehe, ist Ihr env.1 in C

EDIT zu schreiben: scheint wie mir niemand belives ^^, also habe ich eine einfache und schmutzige env.1.c geschrieben:

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