Question

Questions :

  • Que fait le noyau si vous collez un script shell dans la ligne shebang?
  • Comment le noyau sait-il quel interpréteur lancer?

Explication :

Je voulais récemment écrire un wrapper autour de / usr / bin / env car mon environnement CGI ne me permettait pas de définir la variable PATH , sauf globalement (qui Bien sûr, c'est nul!).

Alors j'ai pensé, & "OK. Définissons PREPENDPATH et PATH dans une enveloppe autour de env. & ";". Le script résultant (appelé ici env.1 ) se présente comme suit:

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

qui ressemble à cela devrait fonctionner. J'ai vérifié comment ils réagissaient tous les deux, après avoir réglé 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

Regardez absolument parfait ! Jusqu'ici tout va bien. Mais regardez ce qui arrive à & "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!"

Je suppose qu'il me manque quelque chose d'assez fondamental sur UNIX.

Je suis assez perdu, même après avoir consulté le code source de env d'origine. Il définit l'environnement et lance le programme (ou du moins me semble-t-il ...).

Était-ce utile?

La solution

Tout d’abord, vous devriez très rarement utiliser $* et vous devriez presque toujours utiliser "$@" à la place. Il y a un certain nombre de questions sur SO qui expliquent les tenants et les aboutissants de la raison.

Deuxièmement - la commande env a deux utilisations principales. L'une consiste à imprimer l'environnement actuel; l'autre consiste à contrôler complètement l'environnement d'une commande lorsqu'elle est exécutée. La troisième utilisation, que vous démontrez, est de modifier l’environnement, mais franchement, cela n’est pas nécessaire - les coques sont tout à fait capables de gérer cela pour vous.

Mode 1:

env

Mode 2:

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

Cette version annule toutes les variables d'environnement héritées et exécute command précisément l'environnement défini par les options ENVVAR = value.

Le troisième mode - modification de l'environnement - est moins important, car vous pouvez le faire très bien avec des obus ordinaires (civilisés). (Cela signifie & "Pas le shell C &"; - encore une fois, il y a d'autres questions sur SO qui donnent des réponses qui expliquent cela.) Par exemple, vous pourriez parfaitement faire:

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

Cela insiste sur le fait que $PREPENDPATH est défini sur une chaîne non vide dans l'environnement, qu'il est ensuite ajouté avant $PATH, puis qu'il exporte le nouveau paramètre PATH. Ensuite, en utilisant ce nouveau PATH, il exécute le programme python avec les arguments appropriés. Le exec remplace le script de shell par /usr/bin. Notez que ceci est assez différent de:

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

Superficiellement, c'est la même chose. Cependant, cela exécutera le /home/pi/prepend/bin trouvé sur le PATH préexistant, bien qu'avec la nouvelle valeur de PATH dans l'environnement du processus. Ainsi, dans l’exemple, vous exécuteriez Python à partir de <=> et non celui à partir de <=>.

Dans votre cas, je n'utiliserais probablement pas <=> et utiliserais simplement une variante appropriée du script avec l'exportation explicite.

La commande <=> est inhabituelle car elle ne reconnaît pas le double tiret pour séparer les options du reste de la commande. Ceci est dû en partie au fait qu’il ne prend pas beaucoup d’options et en partie au fait qu’il n’est pas clair si les options ENVVAR = valeur doivent précéder ou suivre le double tiret.

J'ai en fait une série de scripts pour exécuter (différentes versions de) un serveur de base de données. Ces scripts utilisent réellement <=> (et un ensemble de programmes développés en interne) pour contrôler l’environnement du serveur:

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

Autres conseils

Vous devriez lire attentivement l'article de Wikipédia sur shebang .

Lorsque votre système voit le nombre magique correspondant au shebang, il effectue un execve sur le chemin donné après le shebang et donne le script lui-même sous forme d'argument.

Votre script échoue car le fichier que vous fournissez (/usr/bin/env.1) n'est pas un exécutable , mais commence par un shebang ....

Idéalement, vous pouvez résoudre ce problème en utilisant ... env sur votre script avec cette ligne en tant que shebang:

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

Cela ne fonctionnera pas sous Linux, car il traite & "; /usr/bin/env.1 python &"; en tant que chemin (il ne divise pas les arguments)

Le seul moyen que je vois est donc d'écrire votre env.1 en C

EDIT: on dirait que personne ne me croit ^^, alors j’ai écrit un texte simple et sale 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;
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top