Este es uno de los capítulos del tutorial Scripts en Bash. Encontrarás los enlaces a todos los de capítulos, al final de este artículo.
Una de las operaciones mas habituales que se suele hacer a nivel de scripting, es el de encadenar el resultado de un comando con otro comando. Esto es algo que suelo hacer con mucha frecuencia, y que siempre he venido realizando utilizando |
. Sin embargo, existen otros métodos para realizar este encadenamiento, y precisamente en este artículo me quiero centrar en la sustitución de procesos en Bash.
Sustitución de procesos en Bash
¿Que es eso de la sustitución de procesos?
Seguro que en mas de una ocasión te has encontrado algo como <()
, y no, no es una nariz y una boca…. Realmente, lo habrás visto con algo mas, quiero decir, que en el interior de los paréntesis había un comando con sus argumentos etc.
Te pongo un ejemplo, que seguro que en mas de una ocasión es posible que te hayas encontrado,
while read -r linea;do
echo "Hola ${nombre^}"
done < <(echo $data | jq -r '.[] | .nombre')
Si quieres probarlo, puedes utilizar los siguientes datos,
data='[{"nombre":"lorenzo"},{"nombre":"pepe"},{"nombre":"ana"}]'
Esto es simplemente, por ponerte un ejemplo y que hagas una idea de las posibilidaes que te ofrece. Esto mismo, hecho única y exclusivamente con |
se complica ligeramente,
echo $data | jq -r '.[] | .nombre' | xargs -I % echo "Hola %"
Aunque esto mismo podríamos haberlo resuelto de otras mil maneras. Es lo que tiene el maravilloso mundo de la programación y la informática. Lo mismo se puede resolver de mil maneras diferentes.
Un archivo temporal al vuelo…
La sintaxis <(comando)
es conocida como process substitution en Bash, y crea un archivo temporal que contiene la salida del comando especificado como entrada para otro comando. En este caso, < <(ls)
crea un archivo temporal que contiene la salida del comando ls
, y lo utiliza como entrada para el comando while read linea
.
El uso de < <(comando)
es útil en situaciones en las que necesitas redirigir la salida de un comando como entrada para otro comando, pero no quieres crear un archivo temporal en el sistema de archivos. En lugar de eso, < <(comando)
crea un archivo temporal en memoria, que es más eficiente que crear un archivo en disco.
En el caso de <(ls)
se crea un archivo temporal en memoria que contiene la salida del comando ls
. Este archivo temporal en memoria es tratado como un archivo regular por el shell y puede ser utilizado como entrada para otro comando.
Es importante destacar que el archivo temporal creado por <(comando)
no es un archivo real en el sistema de archivos, sino un stream de datos que puede ser utilizado por el shell como si fuera un archivo. Además, el archivo temporal es creado en memoria RAM, lo que lo hace más rápido que crear un archivo en el disco duro.
¿Que es ese espacio en el ejemplo del while?
El espacio entre los dos símbolos <
es necesario en el ejemplo que mencionas, porque indica que se está utilizando una redirección de entrada
(<
) y un process substitution
(<()
) al mismo tiempo.
En Bash, <
se utiliza para redirigir la entrada de un comando desde un archivo o un stream de datos, mientras que <()
se utiliza para crear un archivo temporal en memoria que contiene la salida de un comando.
Cuando se utiliza < <()
, como en el ejemplo que mencionas, el espacio entre los dos símbolos <
indica que se está redirigiendo la salida del archivo temporal generado por <()
como entrada para el comando que sigue a <
.
¿Que usos le podemos dar a la sustitución de procesos?
Como te decía anteriormente, la sustitución de procesos, mpermite utilizar la salida de un comando como entrada de otro comando, pero con la ventaja de que no necesitas crear un archivo temporal. Así, algunos usos que le puedes dar a la sutitución de procesos, son los siguiente,
Comparación de archivos. Puedes utilizar la sustitución de procesos para comparar dos archivos sin necesidad de crear un archivo temporal para cada uno de ellos. Por ejemplo, para comparar el contenido de dos archivos llamados «archivo1.txt» y «archivo2.txt», puedes utilizar el siguiente comando:
diff <(cat archivo1.txt) <(cat archivo2.txt)
Este comando utiliza la sustitución de procesos para pasar la salida del comando «cat archivo1.txt» y «cat archivo2.txt» como entrada para el comando «diff», que compara el contenido de los dos archivos sin necesidad de crear archivos temporales.
Creación de archivos de respaldo. Puedes utilizar la sustitución de procesos para crear archivos de respaldo de forma rápida y eficiente. Por ejemplo, para crear un archivo de respaldo de un archivo llamado «archivo.txt», puedes utilizar el siguiente comando:
cp archivo.txt >(gzip -c > archivo.txt.gz)
En este caso, este comando utiliza la sustitución de procesos para pasar la salida del comando «gzip -c > archivo.txt.gz» como entrada para el comando «cp», que copia el contenido del archivo «archivo.txt» en un archivo temporal que está siendo comprimido y luego redirigido al archivo «archivo.txt.gz».
Ejecución de comandos remotos. Puedes utilizar la sustitución de procesos para ejecutar comandos en un sistema remoto sin necesidad de crear un archivo temporal para transferir la salida del comando. Por ejemplo, para ejecutar el comando «ls» en un sistema remoto con SSH, puedes utilizar el siguiente comando:
ssh usuario@servidor "ls -la" > >(tee salida.txt)
En este caso se utiliza la sustitución de procesos para pasar la salida del comando «ls -la» ejecutado en el sistema remoto como entrada para el comando «tee salida.txt», que guarda la salida en el archivo «salida.txt» y también la muestra en la pantalla.
¿Que diferencia hay entre utilizar |
y ()>
?
La diferencia principal entre el uso del operador «|» (tubería o «pipe») y la sustitución de procesos «<()» es cómo se manejan los datos entre los comandos.
Cuando se utiliza el operador «|» para conectar dos comandos, la salida del primer comando se envía directamente como entrada al segundo comando a través de una tubería. Esto significa que el segundo comando recibirá los datos del primer comando en tiempo real a medida que se producen.
Por otro lado, la sustitución de procesos «<()» se utiliza para capturar la salida de un comando y proporcionarla como entrada a otro comando, pero en forma de un archivo temporal o un descriptor de archivo. La salida del primer comando se almacena temporalmente en un archivo o descriptor de archivo, y luego el segundo comando lo toma como entrada utilizando la sintaxis «< archivo_temporal».
En términos de eficiencia y rendimiento, el uso de la tubería «|» puede ser más eficiente en algunos casos, ya que los datos se transmiten directamente de un comando al siguiente sin necesidad de almacenamiento temporal en un archivo. Sin embargo, puede haber situaciones en las que el uso de la sustitución de procesos sea más conveniente o necesario, como cuando se necesita pasar la salida de un comando a varios comandos diferentes.
¿En que casos es mejor utilizar <()
frente a |
?
- En el caso de que necesites utilizar la salida de un comando en múltiples ubicaciones o comandos diferentes: La sustitución de procesos te permite capturar la salida de un comando y utilizarla como entrada en varios lugares sin tener que ejecutar el comando múltiples veces o almacenar la salida en un archivo temporal.
- Cuando necesitas combinar la salida de múltiples comandos antes de pasarla al siguiente comando: Puedes utilizar la sustitución de procesos para capturar la salida de varios comandos y combinarla antes de proporcionarla como entrada a otro comando. Esto te da más flexibilidad y control sobre cómo se procesan los datos.
- En los casos en los que la salida de un comando es demasiado grande para ser procesada en tiempo real: En algunos casos, la salida de un comando puede ser muy voluminosa y no se puede procesar directamente a través de una tubería «|» debido a limitaciones de memoria o recursos. En tales casos, puedes utilizar la sustitución de procesos para almacenar temporalmente la salida en un archivo o descriptor de archivo y luego procesarla según sea necesario.
- Cuando necesitas combinar comandos que no son compatibles con tuberías: Algunos comandos pueden no ser compatibles con la tubería «|» debido a diferencias en el formato de entrada o salida. En estos casos, puedes utilizar la sustitución de procesos para capturar la salida de un comando y ajustarla o transformarla antes de proporcionarla como entrada a otro comando.
A continuación te muestro algunos ejemplos, donde puedes ver que es mas interesante utilizar la sustitución de procesos <()
antes que el operador pipe |
:
- Utilizar la salida de un comando como argumento de otro comando: Si necesitas pasar la salida de un comando como argumento de otro comando, la sustitución de procesos es más adecuada. Por ejemplo:
grep "patrón" <(cat archivo.txt)
- Realizar operaciones en paralelo: Si deseas realizar operaciones en paralelo en diferentes partes de un archivo o conjunto de datos, la sustitución de procesos puede ser útil. Por ejemplo:
diff <(head -n 1000 archivo1.txt) <(head -n 1000 archivo2.txt)
- Evitar la creación de archivos temporales: Si deseas evitar la creación de archivos temporales para almacenar datos intermedios, la sustitución de procesos es preferible. Por ejemplo:
sort <(grep "patrón" archivo.txt)
Más información,