/ Usr / bin / env Fragen zu shebang Linie Besonderheiten
-
20-08-2019 - |
Frage
Fragen :
- Was ist der Kern tun, wenn Sie einen Shell-Skript in die shebang Linie kleben?
- Wie funktioniert der Kernel wissen, welcher Interpreter zu starten?
Erklärung :
Ich wollte vor kurzem ein Wrapper um schreiben / usr / ist / env , weil meine CGI-Umgebung erlaubt mich nicht, die PATH Variable zu setzen, mit der Ausnahme global (die von natürlich saugt!).
So dachte ich: "OK. Lassen Sie uns gesetzt PREPENDPATH 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 sieht aus wie es funktionieren soll. Ich habe, wie sie beide reagieren nach dem Abbinden 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
Schauen Sie absolut perfekt ! So weit, ist es gut. Aber schauen Sie, was passiert, zu „Hallo Welt!“.
# 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 glaube, ich etwas ziemlich grundlegende über UNIX bin fehlt.
ich ziemlich bin verloren, auch nach dem Quellcode der ursprünglichen Suche env . Er legt die Umwelt und startet das Programm (oder so scheint es mir, ...).
Lösung
Zunächst einmal sollten Sie sehr selten $*
verwenden und Sie sollten fast immer "$@"
stattdessen verwenden. Es gibt eine Reihe von Fragen hier auf SO, die erklären, die Ins und Outs von warum.
Zweitens - der env
Befehl hat zwei Hauptanwendungen. Eine davon ist die aktuelle Umgebung zu drucken; der andere ist vollständig die Umgebung eines Befehls zu steuern, wenn es ausgeführt wird. Die dritte Anwendung, die Sie demonstrieren, ist es, die Umgebung zu ändern, aber ehrlich gesagt gibt es keine Notwendigkeit für die. - die Schalen sind durchaus in der Lage, die für Sie Umgang mit
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 den ENVVAR = Wert-Optionen.
Der dritte Modus - die Umwelt zur Änderung - ist weniger wichtig, weil Sie, dass gut mit regelmäßigen (zivilisierten) Schalen tun können. (Das bedeutet, dass „nicht C-Shell“ - auch hier gibt es andere Fragen mit Antworten auf, so dass das erklären.) Zum Beispiel kann man sehr gut tun:
#!/bin/bash
export PATH=${PREPENDPATH:?}:$PATH
exec python "$@"
Diese besteht darauf, dass $PREPENDPATH
auf eine nicht leere Zeichenfolge in der Umgebung festgelegt ist, und prepends es dann $PATH
und exportiert die neue PATH-Einstellung. Dann, dass die neue PATH verwenden, führt er das python
Programm mit den entsprechenden Argumenten. Die exec
ersetzt den Shell-Skript mit python
. Beachten Sie, dass dies ganz anders aus:
#!/bin/bash
PATH=${PREPENDPATH:?}:$PATH exec python "$@"
Vordergründig ist dies das gleiche. Dies wird jedoch die python
auf der bereits bestehende PATH, wenn auch mit dem neuen Wert von PATH in der Prozessumgebung gefunden auszuführen. So wird in dem Beispiel, würden Sie Python von /usr/bin
und nicht einem von /home/pi/prepend/bin
am Ende ausgeführt wird.
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 env
Befehl ist ungewöhnlich, weil es nicht den Doppel-Bindestrich erkennt Optionen aus dem Rest des Befehls zu trennen. Dies ist zum Teil, weil es nicht viele Optionen, und teilweise nicht nehmen, weil nicht klar ist, ob die ENVVAR = Wert Optionen vor oder nach dem Doppelstrich kommen sollten.
Ich habe tatsächlich eine Reihe von Skripten für den Lauf (verschiedene Versionen) ein Datenbankserver. Diese Skripte wirklich verwendet env
(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 "$@"
case "$*" in
(*v*) track-oninit-v $stdout;;
esac
Andere Tipps
Sie sollten sorgfältig den Wikipedia-Artikel über sheBANG .
Wenn Sie Ihr System die magische Zahl sieht auf den shebang entspricht, es tut einen execve
auf dem angegebenen Pfad nach dem shebang und gibt das Skript selbst als Argument.
Ihr Skript schlägt fehl, da die Datei, die Sie (/usr/bin/env.1
) geben nicht eine ausführbare Datei , sondern fängt an, sich durch eine shebang ....
Im Idealfall könnte man löst es mit ... 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 (es nicht geteilte Argumente)
So ist die einzige Art, wie ich sehe, ist Ihre env.1
in C zu schreiben
EDIT: scheint, wie mich niemand belives ^^, also hat mich einen einfachen und schmutzigen 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;
}