Cuando ejecuto este script, tenía la intención de ejecutar hasta que lo mataran ...
# foo.sh
while true; do sleep 1; done
... No puedo encontrarlo usando ps ax
:
>./foo.sh
// In a separate shell:
>ps ax | grep foo.sh
21110 pts/3 S+ 0:00 grep --color=auto foo.sh
... pero si solo agrego el " #!
" encabezado común al script ...
#! /usr/bin/bash
# foo.sh
while true; do sleep 1; done
... entonces el script se puede encontrar con el mismo ps
comando ...
>./foo.sh
// In a separate shell:
>ps ax | grep foo.sh
21319 pts/43 S+ 0:00 /usr/bin/bash ./foo.sh
21324 pts/3 S+ 0:00 grep --color=auto foo.sh
¿Por qué esto es tan?
Esta puede ser una pregunta relacionada: pensé que " #
" era solo un prefijo de comentario, y si es así " #! /usr/bin/bash
" no es más que un comentario. ¿Pero " #!
" tiene algún significado mayor que un simple comentario?
shell-script
shell
ps
shebang
Lanzamiento De Piedra
fuente
fuente
Respuestas:
Cuando el shell interactivo actual es
bash
, y ejecuta un script sin línea#!
, entoncesbash
ejecutará el script. El proceso se mostrará en laps ax
salida como justobash
.En otra terminal:
Relacionado:
Las secciones relevantes forman el
bash
manual:Esto significa que ejecutar
./foo.sh
en la línea de comandos, cuandofoo.sh
no tiene una línea#!
, es lo mismo que ejecutar los comandos en el archivo en una subshell, es decir, comoCon una línea adecuada
#!
apuntando a/bin/bash
, por ejemplo , es como hacerfuente
ps
muestra el script que se ejecuta como "/usr/bin/bash ./foo.sh
". Entonces, en el primer caso, como usted dice, bash ejecutará el script, pero ¿no sería necesario "pasar" ese script al ejecutable bifurcado bifurcado, como en el segundo caso? (y si es así, me imagino que se podría encontrar con la tubería a grep ...?)$$
todavía apunta al anterior en el caso de subshell (echo $BASHPID
/bash -c 'echo $PPID'
).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é). Laexecve
llamada al sistema reemplaza el código del proceso actualmente en ejecución; Hay una llamadafork
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 errorENOEXEC
(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 agregandops $$
su secuencia de comandos, y más mirando el proceso constrace -p1234 -f -eprocess
1234 donde es el PID del shell.En bash, este mecanismo de respaldo se implementa llamando
fork
pero noexecve
. 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.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.fuente
bash
está bifurcado, tiene acceso a la mismaargv[]
matriz que su padre, que es cómo conoce "los argumentos de la línea de comando original pasados cuando invocó bash originalmente", y si entonces, esta es la razón por la cual el niño no pasa el guión original como un argumento explícito (de ahí que el grep no pueda encontrarlo), ¿es eso correcto?BINFMT_SCRIPT
módulo controla esto y se puede eliminar / modularizar, aunque generalmente está vinculado estáticamente al kernel), pero no veo por qué querría hacerlo, excepto quizás en un sistema integrado . Como una solución para esta posibilidad,bash
tiene un indicador de configuración (HAVE_HASH_BANG_EXEC
) para compensar!ps
informa como argumentos de línea de comando, pero solo hasta cierto punto: tiene que modificar un búfer de memoria existente, no puede agrandar este búfer. Entonces, si bash intentara modificarloargv
para agregar el nombre del script, no siempre funcionaría. Al niño no se le "pasa un argumento" porque nunca hay unaexecve
llamada al sistema en el niño. Es la misma imagen del proceso bash que sigue ejecutándose.En el primer caso, el script es ejecutado por un hijo bifurcado de su shell actual.
Primero debe ejecutar
echo $$
y luego echar un vistazo a un shell que tiene el ID de proceso de su shell como ID de proceso principal.fuente