martes, 22 de agosto de 2017

Sustitución de procesos

Aunque parecen iguales y ambos pueden usarse para combinar secuencias para redireccionarlas, hay una diferencia importante entre los comandos agrupados y los subshells. Mientras que un comando agrupado ejecuta todos sus comandos en el shell actual, un subshell (como su nombre indica) ejecuta sus comandos en una copia hijo del shell actual. Esto significa que el entorno se copia y se pasa a una instancia del shell. Cuando el subshell termina, la copia del entorno se pierde, por lo que cualquier cambio hecho al entorno del subshell (incluyendo la asignación de variables) se pierde también. Por lo tanto, en la mayoría de los casos, a menos que un script requiera un subshell, los comandos agrupados son preferibles a los subshells. Los comandos agrupados son también más rápidos y requieren menos memoria.

Vimos un ejemplo del problema del entorno del subshell en el capítulo 28, cuando descubrimos que el comando read en una tubería no funciona como esperaríamos intuitivamente. Para resumir, si construimos una tubería como esta:

echo "foo" | read
echo $REPLY

El contenido de la variable REPLY siempre está vacío porque el comando read se ejecuta en un subshell, y su copia de REPLY se destruye cuando el subshell termina.

Como los comandos en tuberías siempre se ejecutan en subshells, cualquier comando que asigne variables se encontrará con este problema. Afortunadamente, el shell ofrece una forma exótica de expansión llamada sustitución de procesos que puede usarse para solucionar este problema.

La sustitución de procesos se expresa de dos formas:

Para procesos que producen salida estándar:

<(lista)

o, para procesos que toman entrada estándar:

>(lista)

donde lista es una lista de comandos.

Para resolver nuestro problema con read, podemos emplear sustitución de procesos así:

read < <(echo "foo")
echo $REPLY

La sustitución de procesos nos permite tratar la salida de un subshell como un archivo ordinario para propósitos de redirección. De hecho, como es una forma de expansión, podemos examinar su valor real:

[me@linuxbox ~]$ echo <(echo "foo")
/dev/fd/63

Usando echo para ver el resultado de la expansión, vemos que la salida del subshell está proporcionada por el archivo llamado /dev/fd/63.

La sustitución de procesos se una a menudo con bucles que contienen read. Aquí tenemos un ejemplo de un bucle read que procesa el contenido de un listado de directorios creado por un subshell:

#!/bin/bash

# pro-sub : demo of process substitution

while read attr links owner group size date time filename; do
    cat <<- EOF
        Filename:   $filename
        Size:       $size
        Owner:      $owner
        Group:      $group
        Modified:   $date $time
        Links:      $links
        Attributes: $attr

    EOF
done < <(ls -l | tail -n +2)

El bucle ejecuta read para cada línea de un listado de directorios. El propio listado se produce en la línea final del script. Esta línea redirige la salida de la sustitución de procesos a la entrada estándar del bucle. El comando tail está incluido en la tubería de la sustitución de procesos para eliminar la primera línea del listado, que no se necesita.

Cuando se ejecuta, el script produce una salida como esta:

[me@linuxbox ~]$ pro_sub | head -n 20
Filename:   addresses.ldif
Size:       14540
Owner:      me
Group:      me
Modified:   2009-04-02 11:12
Links:      1
Attributes: -rw-r--r--

Filename:   bin
Size:       4096
Owner:      me
Group:      me
Modified:   2009-07-10 07:31
Links:      2
Attributes: drwxr-xr-x

Filename:   bookmarks.html
Size:       394213
Owner:      me
Group:      me

No hay comentarios:

Publicar un comentario