Otros aspectos de los mecanismos internos del shell
Presentación
Este capítulo está dirigido a lectores que quieran entender en profundidad los mecanismos internos de ciertos comandos. Por lo tanto, leer este capítulo no es indispensable para usar el shell.
Redirigir los descriptores 1 y 2 al mismo archivo
Para enviar la salida estándar y la salida de error estándar al mismo archivo, se debe utilizar una sintaxis especial. Esto es lo que no se debe escribir, junto con la razón de por qué no funciona.
Sintaxis incorrecta
$ comando 1> archivo 2> archivo
$ comando 1> archivo 2>> archivo
El problema no es que se abra el mismo archivo dos veces (lo cual es perfectamente legal dentro del mismo proceso), sino que hay un offset (posición actual en el archivo) asociado con cada apertura. ¿Cuáles son las consecuencias?
-
La secuencia de los resultados en el archivo no será necesariamente representativa del orden en el que se desarrollaron los eventos.
-
Los resultados reportados a través de los descriptores 1 y 2, se pueden superponer.
Las figuras 1 y 2 muestran el mecanismo interno asociado con el siguiente comando:
$ find / -name passwd 1> resu 2> resu
Primera etapa (ver Figura 1)
Procesamiento de la redirección 1>resu:
El shell abre el archivo resu (el archivo se crea con un tamaño de 0) y lo asocia al descriptor 1 (1), (2), (3), (4), (5). Cuando un proceso abre un archivo, siempre hay un registro asignado en la tabla de archivos abiertos del kernel (2) (que agrupa todos los archivos abiertos en el sistema en un momento dado). El registro contiene el modo de apertura del archivo (aquí en modo escritura), así...
Agrupar comandos con paréntesis
En la mayoría de los casos, los paréntesis se utilizan para agrupar comandos.
Sintaxis
(cmdo1; cmdo2; cmdo3)
Con los paréntesis, siempre se crea un shell hijo y es este el que procesa la línea de comandos (con duplicaciones posteriores si es necesario).
Los comandos entre paréntesis se ejecutan desde un shell hijo.
Primer ejemplo
$ (date ; ls) > resultado
$ cat resultado
vie 28 ene 05:21:36 CET 2022
FIC
archivo
$
Las figuras 4, 5 y 6 muestran el mecanismo interno asociado. El shell actual (PID=201) se duplica (1). El shell hijo (PID=205) primero se encarga de la redirección (2), después se duplica para la ejecución del comando externo date (5). Cuando este último ha terminado (7), se duplica de nuevo para ejecutar ls (8). Gracias al mecanismo de herencia, ambos comandos utilizan el mismo offset (3). Por lo tanto, las escrituras en el archivo se suceden entre sí.
Figura 4: Primer ejemplo de agrupación con paréntesis - Primera etapa
Figura 5: Primer ejemplo de agrupación con paréntesis - Segunda etapa
Figura 6: Primer ejemplo de agrupación con paréntesis - Tercera etapa
Segundo ejemplo
El directorio actual los comandos pwd y ls es /tmp:
$ pwd
/home/cristina
$ (cd /tmp ; pwd ; ls) >...
Agrupar comandos con llaves
Sintaxis
{ cmdo1 ; cmdo2 ; cmdo3 ; }
El shell actual procesa la línea de comandos (con duplicaciones posteriores si es necesario).
Primer ejemplo
$ { date; ls ; } > resultado
Las figuras 9, 10, 11 y 12 representan el mecanismo interno asociado a las llaves. El shell de trabajo guarda sus asociaciones actuales descriptor-archivo (1), procesa la redirección solicitada (2), se duplica para la ejecución del comando externo date (5) y luego, cuando el comando externo ha terminado (7), se duplica nuevamente para ejecutar ls (8). Cuando terminan los comandos, el shell de primer nivel toma el control (10) y restaura su entorno descriptor-archivo (11).
Figura 9: Primer ejemplo de agrupación con llaves - Primera etapa
Figura 10: Primer ejemplo de agrupación con llaves - Segunda etapa
Figura 11: Primer ejemplo de agrupación con llaves - Tercera etapa
Figura 12: Primer ejemplo de agrupación con llaves - Cuarta etapa
Segundo ejemplo
Aquí, se modificará el entorno del shell de primer nivel, lo que no es necesariamente muy interesante:
$ pwd
/home/cristina
$
$ { cd /tmp ; pwd ; ls ; } > listaarch
$
$ cat listaarch
/tmp
dcopNYSrKn
listatmp
$ pwd
/tmp
$
Las figuras...
Interpretación de un script mediante un shell hijo
A menos que exista una sintaxis especial, un script es ejecutado por un shell hijo, que mantiene intacto el entorno del shell de trabajo.
Consideremos el siguiente script primero.sh:
$ nl primero.sh
1 pwd
2 cd /tmp
3 pwd
4 ls
$
Echemos un vistazo más de cerca a la ejecución de este script:
$ pwd
/home/cristina
$ primero.sh
/home/cristina
/tmp
f1 f2 f3
$ pwd
/home/cristina
Cuando el script termina de ejecutarse, el valor del directorio actual no ha cambiado. Dado que un script de shell es un comando externo, lo ejecuta un shell hijo. Las figuras 15, 16 y 17 muestran los detalles de la ejecución del script primero.sh.
Figura 15: Mecanismo interno implementado al ejecutar un script de shell - Primera etapa
(1) |
Antes de ejecutar el script, el usuario muestra el valor del directorio actual. Debido a que el comando pwd es un comando interno, lo ejecuta el shell actual (PID=201). Por lo tanto, el resultado del comando es /home/cristina. |
(2) |
Ejecución del script primero. Dado que un script de shell es un comando externo, el shell actual se duplica. Por lo tanto, el script será interpretado por el shell hijo (PID=205). El shell hijo hereda el directorio actual de su padre.... |