permission - Ejecución de script Bash con y sin shebang en Linux y BSD
script permiso denegado (2)
¿Cómo y quién determina qué se ejecuta cuando un script similar a Bash se ejecuta como un binario sin un shebang?
Supongo que la ejecución de un script normal con shebang se maneja con el módulo binfmt_script Linux, que verifica un shebang, analiza la línea de comandos y ejecuta el intérprete de script designado.
Pero, ¿qué sucede cuando alguien ejecuta un script sin un shebang? He probado el enfoque de execv
directo y descubrí que no hay magia del núcleo ahí, es decir, un archivo como ese:
$ cat target-script
echo Hello
echo "bash: $BASH_VERSION"
echo "zsh: $ZSH_VERSION"
Ejecutando el programa compilado de C que hace solo un rendimiento de llamadas execv
:
$ cat test-runner.c void main() { if (execv("./target-script", 0) == -1) perror(); } $ ./test-runner ./target-script: Exec format error
Sin embargo, si hago lo mismo desde otro script de shell, ejecuta el script de destino utilizando el mismo intérprete de shell que el original:
$ cat test-runner.bash #!/bin/bash ./target-script $ ./test-runner.bash Hello bash: 4.1.0(1)-release zsh:
Si hago el mismo truco con otros shells (por ejemplo, sh
- /bin/dash
defecto de Debian), también funciona:
$ cat test-runner.dash #!/bin/dash ./target-script $ ./test-runner.dash Hello bash: zsh:
Misteriosamente, no funciona como se esperaba con zsh y no sigue el esquema general. Parece que zsh ha ejecutado /bin/sh
en dichos archivos después de todo:
greycat@burrow-debian ~/z/test-runner $ cat test-runner.zsh #!/bin/zsh echo ZSH_VERSION=$ZSH_VERSION ./target-script greycat@burrow-debian ~/z/test-runner $ ./test-runner.zsh ZSH_VERSION=4.3.10 Hello bash: zsh:
Tenga en cuenta que ZSH_VERSION
en el script de los padres funcionó, mientras que ZSH_VERSION
en el niño no lo hizo.
¿Cómo un shell (Bash, dash) determina qué se ejecuta cuando no hay shebang? He intentado desenterrar ese lugar en fuentes Bash / dash, pero, por desgracia, parece que estoy algo perdido allí. ¿Alguien puede arrojar algo de luz sobre la magia que determina si el archivo de destino sin shebang debe ejecutarse como script o como binario en Bash / dash? ¿O puede que haya algún tipo de interacción con kernel / libc y luego me gustaría recibir explicaciones sobre cómo funciona en Linux y FreeBSD kernels / libcs?
(Parece que Sorpigal lo ha cubierto, pero ya lo he escrito y puede ser de interés).
De acuerdo con la Sección 3.16 de las Preguntas Frecuentes de Unix , el shell primero mira el número mágico (los dos primeros bytes del archivo). Algunos números indican un ejecutable binario; #!
indica que el resto de la línea debe interpretarse como un shebang. De lo contrario, el shell intenta ejecutarlo como un script de shell.
Además, parece que csh
mira el primer byte, y si es #
, intentará ejecutarlo como un script csh
.
Como esto sucede en el tablero, el tablero es más simple, miré allí primero.
Parece que exec.c es el lugar para buscar, y la función relevante es tryexec
, que se llama desde shellexec
y se llama siempre que el shell tiene que ejecutar un comando. Y (una versión simplificada de) la función tryexec es la siguiente:
STATIC void
tryexec(char *cmd, char **argv, char **envp)
{
char *const path_bshell = _PATH_BSHELL;
repeat:
execve(cmd, argv, envp);
if (cmd != path_bshell && errno == ENOEXEC) {
*argv-- = cmd;
*argv = cmd = path_bshell;
goto repeat;
}
}
Por lo tanto, simplemente siempre reemplaza el comando para ejecutarse con la ruta a sí mismo ( _PATH_BSHELL
defecto es "/bin/sh"
) si se produce ENOEXEC
. Realmente no hay magia aquí.
Encuentro que FreeBSD exhibe un comportamiento idéntico en bash
y en su propio sh
.
La forma en que bash
maneja esto es similar pero mucho más complicada. Si desea consultar más detalladamente, le recomiendo que lea bash execute_command.c
y que busque específicamente execute_shell_script
y luego shell_execve
. Los comentarios son bastante descriptivos.