GNU/Linux >> Tutoriales Linux >  >> Linux

¿Cómo lidia Linux con los scripts de shell?

Si usa strace puede ver cómo se ejecuta un script de shell cuando se ejecuta.

Ejemplo

Digamos que tengo este script de shell.

$ cat hello_ul.bash 
#!/bin/bash

echo "Hello Unix & Linux!"

Ejecutarlo usando strace :

$ strace -s 2000 -o strace.log ./hello_ul.bash
Hello Unix & Linux!
$

Echando un vistazo dentro del strace.log archivo revela lo siguiente.

...
open("./hello_ul.bash", O_RDONLY)       = 3
ioctl(3, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, 0x7fff0b6e3330) = -1 ENOTTY (Inappropriate ioctl for device)
lseek(3, 0, SEEK_CUR)                   = 0
read(3, "#!/bin/bash\n\necho \"Hello Unix & Linux!\"\n", 80) = 40
lseek(3, 0, SEEK_SET)                   = 0
getrlimit(RLIMIT_NOFILE, {rlim_cur=1024, rlim_max=4*1024}) = 0
fcntl(255, F_GETFD)                     = -1 EBADF (Bad file descriptor)
dup2(3, 255)                            = 255
close(3)     
...

Una vez que se ha leído el archivo, se ejecuta:

...
read(255, "#!/bin/bash\n\necho \"Hello Unix & Linux!\"\n", 40) = 40
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 3), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc0b38ba000
write(1, "Hello Unix & Linux!\n", 20)   = 20
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
read(255, "", 40)                       = 0
exit_group(0)                           = ?

En lo anterior, podemos ver claramente que todo el script parece leerse como una sola entidad y luego ejecutarse allí después. Entonces "aparecería" al menos en el caso de Bash que lee el archivo y luego lo ejecuta. Entonces, ¿pensarías que podrías editar el script mientras se está ejecutando?

NOTA: ¡Pero no lo hagas! Siga leyendo para comprender por qué no debe meterse con un archivo de script en ejecución.

¿Qué pasa con otros intérpretes?

Pero tu pregunta está un poco fuera de lugar. No es Linux el que carga necesariamente el contenido del archivo, es el intérprete el que carga el contenido, por lo que realmente depende de cómo se implemente el intérprete, ya sea que cargue el archivo por completo o en bloques o líneas a la vez.

Entonces, ¿por qué no podemos editar el archivo?

Sin embargo, si usa un script mucho más grande, notará que la prueba anterior es un poco engañosa. De hecho, la mayoría de los intérpretes cargan sus archivos en bloques. Esto es bastante estándar con muchas de las herramientas de Unix donde cargan bloques de un archivo, lo procesan y luego cargan otro bloque. Puedes ver este comportamiento con estas preguntas y respuestas de U&L que escribí hace un tiempo con respecto a grep , titulado:¿Cuánto texto consume grep/egrep cada vez?.

Ejemplo

Digamos que hacemos el siguiente script de shell.

$ ( 
    echo '#!/bin/bash'; 
    for i in {1..100000}; do printf "%s\n" "echo \"$i\""; done 
  ) > ascript.bash;
$ chmod +x ascript.bash

Resultando en este archivo:

$ ll ascript.bash 
-rwxrwxr-x. 1 saml saml 1288907 Mar 23 18:59 ascript.bash

Que contiene el siguiente tipo de contenido:

$ head -3 ascript.bash ; echo "..."; tail -3 ascript.bash 
#!/bin/bash
echo "1"
echo "2"
...
echo "99998"
echo "99999"
echo "100000"

Ahora, cuando ejecute esto usando la misma técnica anterior con strace :

$ strace -s 2000 -o strace_ascript.log ./ascript.bash
...    
read(255, "#!/bin/bash\necho \"1\"\necho \"2\"\necho \"3\"\necho \"4\"\necho \"5\"\necho \"6\"\necho \"7\"\necho \"8\"\necho \"9\"\necho \"10\"\necho 
...
...
\"181\"\necho \"182\"\necho \"183\"\necho \"184\"\necho \"185\"\necho \"186\"\necho \"187\"\necho \"188\"\necho \"189\"\necho \"190\"\necho \""..., 8192) = 8192

Notará que el archivo se lee en incrementos de 8 KB, por lo que es probable que Bash y otros shells no carguen un archivo en su totalidad, sino que lo lean en bloques.

Referencias

  • El #! magia, detalles sobre el mecanismo shebang/hash-bang en varios sabores de Unix

Esto depende más del shell que del sistema operativo.

Dependiendo de la versión, ksh lea el script a pedido por bloque de 8k o 64k bytes.

bash Lea el guión línea por línea. Sin embargo, dado el hecho de que las líneas pueden tener una longitud arbitraria, lee cada vez 8176 bytes desde el comienzo de la siguiente línea para analizar.

Esto es para construcciones simples, es decir, un conjunto de comandos sencillos.

Si se usan comandos estructurados de shell (un caso que la respuesta aceptada no se debe considerar ) como un for/do/done bucle, un case/esac switch, un documento aquí, una subcapa encerrada entre paréntesis, una definición de función, etc. y cualquier combinación de los anteriores, los intérpretes de la consola leen hasta el final de la construcción para asegurarse primero de que no haya ningún error de sintaxis.

Esto es algo ineficiente ya que el mismo código se puede leer una y otra vez una gran cantidad de veces, pero se ve mitigado por el hecho de que este contenido normalmente se almacena en caché.

Cualquiera que sea el intérprete de shell, es muy imprudente modificar un script de shell mientras se está ejecutando, ya que el shell puede volver a leer cualquier parte del script y esto puede provocar errores de sintaxis inesperados si no está sincronizado.

Tenga en cuenta también que bash puede bloquearse con una infracción de segmentación cuando no puede almacenar una construcción de script demasiado grande que ksh93 puede leer sin problemas.


Eso depende de cómo funcione el intérprete que ejecuta el script. Todo lo que hace el kernel es notar que el archivo a ejecutar comienza con #! , esencialmente ejecuta el resto de la línea como un programa y le da el ejecutable como argumento. Si el intérprete enumerado allí lee ese archivo línea por línea (como lo hacen los shells interactivos con lo que escribe), eso es lo que obtiene (pero las estructuras de bucle de varias líneas se leen y se mantienen para repetir); si el intérprete absorbe el archivo en la memoria, lo procesa (quizás lo compila en una representación intermedia, como lo hacen Perl y Pyton), el archivo se lee por completo antes de ejecutarse.

Si elimina el archivo mientras tanto, el archivo no se elimina hasta que el intérprete lo cierra (como siempre, los archivos desaparecen cuando desaparece la última referencia, ya sea una entrada de directorio o un proceso que lo mantiene abierto).


Linux
  1. Cómo uso Vagrant con libvirt

  2. Cómo cifrar archivos con gocryptfs en Linux

  3. Conceptos básicos de Linux:cómo descargar archivos en el Shell con Wget

  4. Linux:¿cómo funciona el promedio de carga con las CPU modernas?

  5. ¿Cómo analizar Json con secuencias de comandos de Shell en Linux?

Cómo comparar directorios con Meld en Linux

Cómo ejecutar un comando de Shell con Python

Shell Scripting para principiantes:cómo escribir Bash Scripts en Linux

Cómo asegurar servidores Linux con SE Linux

Cómo cambiar un Shell de usuario en Linux

¿Cómo usar if-else en Shell Scripts?