Este es un pequeño truco que, en ocasiones me ha salvado un poco de tiempo. Un mini-post que tenía ganas de compartir por aquí y que hoy voy a empezar por el final. Es decir, poniendo el código:
Con esto, ejecutamos el comando 1, luego el comando 2 y se lo pasamos a diff en forma de pipe creada expresamente para la ocasión. Es decir, la salida del comando 1 se va almacenando en una pipe, y la salida del comando 2 se va almacenando en otra pipe. Dichas pipes, en Linux las podemos encontrar en /proc/[PID del proceso]/fd/
¿Para qué me vale esto?
Saber si dos directorios tienen los mismos archivos
Por ejemplo, si tenemos dos directorios que tienen que tener un contenido parecido (los mismos archivos) y queremos saber si falta alguno, aunque podemos utilizar meld, si lo queremos de forma gráfica, si queremos un resultado más rápido o no disponemos de GUI, podemos utilizar:
Incluso si es un directorio remoto al cual accederemos por ssh:
Esto puede darnos muchas posibilidades.
Saber si dos sumas MD5 coinciden
Otro pequeño ejemplo puede ser este, en el que comprobamos las sumas MD5 de dos archivos o grupos de archivos y tenemos que ver si son iguales o no:
Lógicamente si el ejemplo anterior no nos devuelve nada, algo estaría mal, o habríamos descubierto otro problema más del algoritmo MD5, pero es sólo un ejemplo, si comprobamos dos archivos cuyo contenido debe ser idéntico tendría más sentido.
En nuestros programas
Bueno, si queremos hacer un programa que soporte esto, no tenemos que hacer absolutamente nada, ya que las pipes se transmiten como nombres de archivos, así que si nuestro programa soporta la entrada de nombres de archivo como argumentos, podemos utilizar nuestro programa sin mieto con estas técnicas.
Es más, si hacemos un programa como este:
No estoy comprobando nada, sólo muestro el primer argumento, si lo ejecutamos, veremos algo como esto:
y, claro, /dev/fd/63 (que será un enlace a /proc/PID/fd/63 en Linux, al menos), y dicho archivo que en realidad es una named pipe, lo podemos abrir como un archivo y tratarlo como tal. La única salvedad es que no podemos navegar por él, es decir, no podemos leer varias veces lo mismo, una vez que esos datos se leen, se perderán. Y eso no es malo, si por ejemplo, por tu pipe pasan grandísimas cantidades de datos (incluso terabytes), no querrás que se llene tu memoria o tener que borrarlos expresamente, el SO lo hace por ti.
Oye, pero irá algo lento, ¿no?
Depende de cómo esté construido tu programa, en el caso de diff no tiene por qué ir lento. Me explicaré un poco mejor, esto tiene más que ver con el comportamiento de las pipes. Cuando estás leyendo una pipe, no recibirás el EOF ( end of file) hasta que sea imposible que la pipe contenga datos, eso es, hasta que el proceso que la está escribiendo no la cierre del todo, mientras esas pipes no se cierren, el proceso que lee de la pipe quedará bloqueado (creo que algún día haré un post contando la vida de las pipes).
Entonces, lo que quiero decir es que el proceso no es exactamente:
- Ejecuta el primero y lo guarda todo
- Ejecuta el segundo y lo guarda todo
- Ejecuta el comando principal (diff en este caso) con la info guardada por los procesos anteriores
Ni mucho menos, podemos ganar mucho más rendimiento, si lo hacemos todo a la vez... es decir:
- Creo la pipe para el proceso 1, lo ejecuto y que vaya guardando allí la salida
- Antes de que termine el proceso 1, ya tengo la pipe del proceso 2 preparada, y ejecuto el proceso 2 de la misma manera
- Antes de que terminen los dos procesos anteriores, ejecuto el proceso 3 y le paso las pipes para que las vaya leyendo
Esto tiene varias ventajas:
- Mucha más velocidad, ya que no hay esperas a los procesos que generan la salida
- Los procesos pueden generar gran cantidad de datos y no saturamos la memoria, porque a medida que se va generando, el comando principal (diff) se la va comiendo y el SO la va liberando
Lo sé, una vez que conoces esto, ya no eres capaz de hacerlo a la antigua usanza:
Foto principal: CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=1864947