Domanda

Domande :

  • Cosa fa il kernel se si inserisce uno script shell nella riga shebang?
  • Come fa il kernel a sapere quale interprete avviare?

Spiegazione :

Di recente ho voluto scrivere un wrapper su / usr / bin / env perché il mio ambiente CGI non mi consente di impostare la variabile PATH , tranne globalmente ovviamente fa schifo!).

Quindi ho pensato, " OK. Impostiamo PREPENDPATH e impostiamo PERCORSO in un involucro attorno a env. & Quot ;. Lo script risultante (qui chiamato env.1 ) era simile al seguente:

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

che sembra che dovrebbe funzionare. Ho verificato come reagiscono entrambi, dopo aver impostato 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

Sembra assolutamente perfetto ! Fin qui tutto bene. Ma guarda cosa succede a & Quot; Hello World! & Quot ;.

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

Suppongo che mi manchi qualcosa di piuttosto fondamentale su UNIX.

Sono piuttosto perso, anche dopo aver visto il codice sorgente dell'originale env . Imposta l'ambiente e avvia il programma (o almeno così mi sembra ...).

È stato utile?

Soluzione

Prima di tutto, dovresti raramente usare $* e invece dovresti quasi sempre usare "$@". Ci sono una serie di domande qui su SO che spiegano i dettagli del perché.

Secondo: il comando env ha due usi principali. Uno è stampare l'ambiente attuale; l'altro è controllare completamente l'ambiente di un comando quando viene eseguito. Il terzo uso, che stai dimostrando, è modificare l'ambiente, ma francamente non ce n'è bisogno - le shell sono abbastanza capaci di gestirlo per te.

Modalità 1:

env

Modalità 2:

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

Questa versione annulla tutte le variabili d'ambiente ereditate ed esegue command esattamente con l'ambiente impostato dalle opzioni ENVVAR = value.

La terza modalità - che modifica l'ambiente - è meno importante perché puoi farlo bene con shell normali (civili). (Ciò significa & Quot; non C shell & Quot; - di nuovo, ci sono altre domande su SO con risposte che lo spiegano.) Ad esempio, potresti perfettamente fare:

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

Ciò insiste sul fatto che $PREPENDPATH è impostato su una stringa non vuota nell'ambiente, quindi lo antepone a $PATH ed esporta la nuova impostazione PATH. Quindi, usando quel nuovo PERCORSO, esegue il programma python con gli argomenti pertinenti. exec sostituisce lo script della shell con /usr/bin. Nota che questo è abbastanza diverso da:

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

Superficialmente, questo è lo stesso. Tuttavia, questo eseguirà il /home/pi/prepend/bin trovato sul PERCORSO preesistente, sebbene con il nuovo valore di PERCORSO nell'ambiente di processo. Quindi, nell'esempio, finiresti per eseguire Python da <=> e non quello da <=>.

Nella tua situazione, probabilmente non userei <=> e utilizzerei solo una variante appropriata dello script con l'esportazione esplicita.

Il comando <=> è insolito perché non riconosce il doppio trattino per separare le opzioni dal resto del comando. Ciò è in parte dovuto al fatto che non richiede molte opzioni, e in parte perché non è chiaro se le opzioni ENVVAR = value debbano venire prima o dopo il doppio trattino.

In realtà ho una serie di script per eseguire (diverse versioni di) un server di database. Questi script usano davvero <=> (e un sacco di programmi fatti in casa) per controllare l'ambiente del server:

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

Altri suggerimenti

Dovresti leggere attentamente l'articolo di Wikipedia su shebang .

Quando il tuo sistema vede il numero magico corrispondente allo shebang, fa un execve sul percorso dato dopo lo shebang e fornisce lo script stesso come argomento.

Il tuo script fallisce perché il file che dai (/usr/bin/env.1) non è un eseguibile , ma inizia da solo con un shebang ....

Idealmente, potresti risolverlo usando ... env sul tuo script con questa riga come shebang:

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

Non funzionerà su Linux poiché tratta " /usr/bin/env.1 python " come percorso (non divide argomenti)

Quindi l'unico modo che vedo è scrivere il tuo env.1 in C

EDIT: sembra che nessuno mi creda ^^, quindi ho scritto un semplice e sporco 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;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top