Cuando comienza un script de shell #!
, esa primera línea es un comentario en lo que respecta al shell. Sin embargo, los dos primeros caracteres son significativos para otra parte del sistema: el núcleo. Los dos personajes #!
se llaman shebang . Para comprender el papel del shebang, debe comprender cómo se ejecuta un programa.
Ejecutar un programa desde un archivo requiere una acción del núcleo. Esto se realiza como parte de la execve
llamada al sistema. El núcleo debe verificar los permisos del archivo, liberar los recursos (memoria, etc.) asociados al archivo ejecutable que se está ejecutando actualmente en el proceso de llamada, asignar recursos para el nuevo archivo ejecutable y transferir el control al nuevo programa (y más cosas que No lo mencionaré). La execve
llamada al sistema reemplaza el código del proceso actualmente en ejecución; Hay una llamada fork
al sistema por separado para crear un nuevo proceso.
Para hacer esto, el núcleo debe admitir el formato del archivo ejecutable. Este archivo tiene que contener código de máquina, organizado de manera que el núcleo lo entienda. Un script de shell no contiene código de máquina, por lo que no se puede ejecutar de esta manera.
El mecanismo shebang permite que el núcleo difiera la tarea de interpretar el código a otro programa. Cuando el núcleo ve que el archivo ejecutable comienza con #!
, lee los siguientes caracteres e interpreta la primera línea del archivo (menos el #!
espacio inicial y opcional) como una ruta a otro archivo (más argumentos, que no discutiré aquí) ) Cuando se le dice al núcleo que ejecute el archivo /my/script
, y ve que el archivo comienza con la línea #!/some/interpreter
, el núcleo se ejecuta /some/interpreter
con el argumento /my/script
. Entonces depende de /some/interpreter
decidir que /my/script
es un archivo de script que debe ejecutar.
¿Qué sucede si un archivo no contiene código nativo en un formato que el kernel entienda y no comience con un shebang? Bueno, entonces el archivo no es ejecutable y la execve
llamada al sistema falla con el código de error ENOEXEC
(error de formato ejecutable).
Este podría ser el final de la historia, pero la mayoría de los shells implementan una función alternativa. Si el kernel regresa ENOEXEC
, el shell mira el contenido del archivo y comprueba si parece un script de shell. Si el shell cree que el archivo parece un script de shell, lo ejecuta por sí mismo. Los detalles de cómo hacerlo depende del shell. Puede ver algo de lo que está sucediendo agregando ps $$
su secuencia de comandos, y más mirando el proceso con strace -p1234 -f -eprocess
1234 donde es el PID del shell.
En bash, este mecanismo de respaldo se implementa llamando fork
pero no execve
. El proceso bash secundario borra su estado interno por sí mismo y abre el nuevo archivo de script para ejecutarlo. Por lo tanto, el proceso que ejecuta el script sigue utilizando la imagen del código bash original y los argumentos de la línea de comando original pasados cuando invocó bash originalmente. ATT ksh se comporta de la misma manera.
% bash --norc
bash-4.3$ ./foo.sh
PID TTY STAT TIME COMMAND
21913 pts/2 S+ 0:00 bash --norc
Dash, en contraste, reacciona ENOEXEC
llamando /bin/sh
con la ruta al guión pasado como argumento. En otras palabras, cuando ejecuta un script shebangless desde el tablero, se comporta como si el script tuviera una línea shebang #!/bin/sh
. Mksh y zsh se comportan de la misma manera.
% dash
$ ./foo.sh
PID TTY STAT TIME COMMAND
21427 pts/2 S+ 0:00 /bin/sh ./foo.sh