2008-12-09 10 views
6

Preguntas:preguntas/usr/bin/env respecto a peculiaridades de línea tinglado

  • Lo que hace el kernel hacer si usted se pega un shell-script en la línea tinglado?
  • ¿Cómo sabe el Kernel qué intérprete lanzar?

Explicación:

recientemente he querido escribir una envoltura alrededor de /usr/bin/env porque mi entorno CGI no me permite establecer la variable de PATH, excepto a nivel mundial (que por supuesto, es una mierda!).

Así que pensé, "OK, establezcamos PREPENDPATH y establezcamos PATH en un contenedor alrededor de env.". El guión resultante (llamada aquí env.1) era la siguiente:

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

que parece que debería funcionar. Comprobé cómo reaccionan tanto, después de establecer 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 

parecen absolutamente perfecta ! Hasta aquí todo bien. Pero mira lo que le sucede a "¡Hola mundo!".

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

Supongo que me falta algo muy fundamental acerca de UNIX.

Estoy bastante perdido, incluso después de mirar el código fuente original env. Establece el entorno y lanza el programa (o eso me parece a mí ...).

Respuesta

6

En primer lugar, rara vez debe utilizar $* y casi siempre debe usar "[email protected]" en su lugar. Hay una serie de preguntas aquí en SO que explican los pormenores de por qué.

Segundo: el comando env tiene dos usos principales. Una es imprimir el entorno actual; el otro es controlar completamente el entorno de un comando cuando se ejecuta. El tercer uso, que está demostrando, es modificar el entorno, pero francamente no hay necesidad de eso; los proyectiles son bastante capaces de manejar eso por usted.

Modo 1:

env 

Modo 2:

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

Esta versión cancela todas las variables de entorno heredados y se ejecuta command con precisión el entorno establecido por las opciones ENVVAR = valor.

El tercer modo, que modifica el entorno, es menos importante porque puedes hacerlo bien con las conchas normales (civilizadas). (. Esto significa "no shell C" - de nuevo, hay otras preguntas sobre SO con las respuestas que explican que) Por ejemplo, usted podría hacer perfectamente bien:

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

Este insiste en que $PREPENDPATH se establece en un país que no cadena vacía en el entorno, y luego lo antepone al $PATH, y exporta la nueva configuración RUTA. Luego, utilizando esa nueva RUTA, ejecuta el programa python con los argumentos relevantes. El exec reemplaza el script de shell con python. Tenga en cuenta que esto es bastante diferente de:

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

Superficialmente, esto es lo mismo. Sin embargo, esto ejecutará el python encontrado en el PATH preexistente, aunque con el nuevo valor de PATH en el entorno del proceso. Entonces, en el ejemplo, terminará ejecutando Python desde /usr/bin y no desde el /home/pi/prepend/bin.

En su situación, probablemente no usaría env y solo usaría una variante apropiada de la secuencia de comandos con la exportación explícita.

El comando env es inusual porque no reconoce el doble guión para separar las opciones del resto del comando. Esto se debe en parte a que no requiere muchas opciones, y en parte porque no está claro si las opciones ENVVAR = value deben aparecer antes o después del doble guión.

De hecho, tengo una serie de scripts para ejecutar (diferentes versiones de) un servidor de base de datos. Estos scripts realmente utilizan env (y un montón de programas de cosecha propia) para controlar el entorno del servidor:

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

Esta información es útil, pero como dice el PO, su/su núcleo no se ejecutará no binarios (como los sufragios bash que proporcionas) como primer elemento de una línea shebang. El mío tampoco lo hará; específicamente, el núcleo silenciosamente falla al ejecutar el script con la línea de shebang problemática en el intérprete deseado (no binario), y luego mi caparazón intentará ejecutar el script en su lugar. (Entiendo que este es un comportamiento de shell antiguo que conservan muchos shells.) – dubiousjim

+0

¿No entiendo lo que dices? ¿Estás afirmando que hay (todavía) conchas o núcleos que no manejarán '#!/Bin/bash' o' #!/Bin/ksh' como la primera línea de un script? No estoy afirmando ni mostrando que puedes usar '#!/Some/script' como shebang. –

+0

No reclamando el primero. Sin embargo, la pregunta del OP es sobre el segundo: preguntó: "¿Qué hace el kernel si insertas un guión de shell en la línea shebang?" y luego informa problemas que provienen de usar env.1 como una línea shebang, donde env.1 es un script. (Da la casualidad de que estaba buscando discusiones sobre las limitaciones de las líneas shebang que me trajeron aquí. Y encontré que tu publicación contenía información útil, así que gracias por contribuir. Estaba señalando, para otros lectores que vendrán después, que lo que discuten no ayuda a superar la limitación con la que OP se había enfrentado). – dubiousjim

4

Lea atentamente el artículo de wikipedia sobre shebang.

Cuando su sistema ve el número mágico correspondiente al shebang, hace un execve en la ruta dada después del shebang y le da el script como un argumento.

Su script falla porque el archivo que das (/usr/bin/env.1) no es un archivo ejecutable , pero sí empieza por un tinglado ....

Idealmente, usted podría resolverlo a través de ...env en su guión con esta línea como un tinglado:

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

no va a funcionar, aunque en Linux, ya que trata "/usr/bin/env.1 python" como un camino (que no se divide argumentos)

Así que la única mi modo de ver es escribir su env.1 en C

EDIT: parece como si nadie me ^^ belives, por lo que he escrito un simple y sucio 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; 
} 
Cuestiones relacionadas