/usr/bin/env preguntas sobre el proceso de la línea de peculiaridades
-
20-08-2019 - |
Pregunta
Preguntas:
- ¿Qué hace el kernel de hacer si usted se pega un shell-script en el shebang línea?
- ¿Cómo funciona el Kernel de saber que el intérprete para el lanzamiento?
Explicación:
Recientemente he querido escribir una envoltura alrededor de /usr/bin/env porque mi Entorno CGI no me permite establecer el CAMINO variable, excepto a nivel mundial (que por supuesto es una mierda!).
Así que pensé, "OK.Vamos a establecer PREPENDPATH y establecer CAMINO en un contenedor de env.".La secuencia de comandos resultante (aquí llamado env.1) se veía así:
#!/bin/bash
/usr/bin/env PATH=$PREPENDPATH:$PATH $*
que parece que debería funcionar.He comprobado cómo se reacciona, 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 perfecto!Tan lejos, tan bueno.Pero veamos lo que ocurre en "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!"
Supongo que me estoy perdiendo algo muy fundamental sobre UNIX.
Estoy bastante perdido, incluso después de mirar el código fuente de la original env.Se establece el entorno y lanza el programa (o eso me parece...).
Solución
Primero de todos, usted debe muy rara vez uso $*
y casi siempre uso "$@"
en su lugar.Hay una serie de preguntas sobre el MODO en el que se explican los pormenores de por qué.
Segundo - la env
comando tiene dos usos principales.Uno es para imprimir el entorno actual;la otra es controlar completamente el entorno de un comando cuando se ejecuta.El tercer uso, que están demostrando, es modificar el medio ambiente, pero, francamente, no hay necesidad de que - las conchas son muy capaces de manejar para usted.
Modo 1:
env
Modo 2:
env -i HOME=$HOME PATH=$PREPENDPATH:$PATH ... command args
Esta versión cancela todos los heredados de las variables de entorno y se ejecuta command
precisamente con el medio ambiente establecido por la ENVVAR=valor de las opciones.
El tercer modo - que modifica el medio ambiente - es menos importante porque puede hacerlo bien con regular (civilizado) las conchas.(Que significa "no shell C" - de nuevo, hay otras preguntas PARA las respuestas que explicar eso.) Por ejemplo, usted podría perfectamente hacer:
#!/bin/bash
export PATH=${PREPENDPATH:?}:$PATH
exec python "$@"
Este insiste en que $PREPENDPATH
se establece una cadena no vacía en el medio ambiente, y, a continuación, se antepone a $PATH
, y las exportaciones de la nueva configuración de la RUTA.Luego, utilizando la nueva RUTA de acceso, se ejecuta el python
programa con los argumentos relevantes.El exec
sustituye a la secuencia de comandos de shell con python
.Tenga en cuenta que esto es muy diferente de:
#!/bin/bash
PATH=${PREPENDPATH:?}:$PATH exec python "$@"
Superficialmente, esta es la misma.Sin embargo, se ejecutará el python
se encuentran en la pre-RUTA existente, aunque con el nuevo valor de la RUTA de acceso en el proceso del medio ambiente.Así, en el ejemplo, se acabaría la ejecución de Python desde /usr/bin
y no el de /home/pi/prepend/bin
.
En su situación, probablemente no lo haría uso env
y podría usar una variante adecuada de la secuencia de comandos con la explícita a la exportación.
El env
comando es inusual porque no reconoce la doble guion para separar las opciones en el resto de la orden.Esto es en parte porque no tiene muchas opciones, y en parte porque no está claro si el ENVVAR=valor de las opciones debe venir antes o después de la doble guión.
La verdad es que tengo una serie de scripts para la ejecución de (diferentes versiones) de un servidor de base de datos.Estas secuencias de comandos a usar realmente env
(y un montón de inicio-programas desarrollados) 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 "$@"
case "$*" in
(*v*) track-oninit-v $stdout;;
esac
Otros consejos
Debe leer detenidamente 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 al script en sí mismo un argumento.
Tu script falla porque el archivo que le das (/usr/bin/env.1
) no es un ejecutable , sino que comienza con un shebang ...
Idealmente, podría resolverlo usando ... env
en su secuencia de comandos con esta línea como un shebang:
#!/usr/bin/env /usr/bin/env.1 python
Sin embargo, no funcionará en Linux, ya que trata " /usr/bin/env.1 python
" como una ruta (no divide argumentos)
Entonces, la única forma en que veo es escribir su env.1
en C
EDITAR: parece que nadie me cree ^^, así 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;
}