\
FIFO. FIFO es un acrónimo de First In First Out, que se refiere a la propiedad de que los bytes salen con el mismo orden que entran. El "name" en named pipe es en verdad el nombre de un archivo. Los archivos tipo named pipe son mostrados por el comando "ls" como cualquier otro, con pocas diferencias, mira:
p en la columna del lado izquierdo indica que pipe1 es un named pipe. El resto de los bits de control de permisos, que se pueden leer o grabar al pipe, funcionan como un archivo normal. En los sistemas mas modernos una barra vertical (|) colocada al final del nombre del archivo, es otra pista y en los sistemas LINUX, donde la opción de color esta habilitada, el nombre del archivo se escribe en rojo por defecto.
En los sistemas mas antiguos, los named pipes son creados por el programa mknod, normalmente situado en el directorio /etc.
En los sistemas mas modernos, la misma tarea es hecha por mkfifo. EL programa mkfifo recibe uno o mas nombres como argumento y crea pipes con esos nombres. Por ejemplo , para crear un named pipe con nombre pipe1 haz:
named pipe mostrado anteriormente. Vamos ahora a trabajar con dos secciones o dos consolas virtuales o una de cada una. En una de ellas haz:
ls era el que hablaba y el cat era el que escuchaba.-
Una aplicación muy útil de los named pipes es permitir que programas sin ninguna relación se puedan comunicar entre sí, los named pipes también son usados para sincronizar procesos, ya que en un determinado punto puedes colocar un proceso para "escuchar" o "hablar" en un determinado named pipe y solo saldrá de allí , si otro proceso "habla" o "escucha" en aquel pipe.
Viste que el uso de esta herramienta es ideal para sincronizar los procesos y para bloquear archivos y poder evitar así
perdida o corrupción de información debido a actualizaciones simultáneas (concurrencia). Veamos ejemplos para ilustrar estos casos.
BLOCO1 del Programa1 las tres clasificaciones son lanzadas de la siguiente manera:
for Arq in BigFile1 BigFile2 BigFile3
do
if sort $Arq
then
Manda=va
else
Manda=pare
break
fi
done
echo $Manda > pipe1
[ $Manda = pare ] &&
{
echo Error durante la clasificación de los archivos
exit 1
}
...
De esta forma, el comando if verifica cada clasificación que está siendo efectuada. En caso de que ocurra un problema, las clasificaciones siguientes serán abortadas, un mensaje conteniendo la cadena pare es enviada por el pipe1 y el programa1 es finalizado con un fin anormal.
Mientras el Programa1 ejecutaba su primero bloque (las clasificaciones) el Programa2 ejecutaba su bloque BLOCO1, procesando sus rutinas de apertura y menú paralelamente al Programa1, ganando de esta forma un buen intervalo de tiempo.
El fragmento del código del Programa2 que vemos a continuación, muestra la transición del BLOCO1 hacia el BLOCO2:
OK=`cat pipe1`
if [ $OK = va ]
then
...
Rutina de impresión
...
else # Recibí "pare" en OK
exit 1
fi
Después de la ejecución del primer bloque , el Programa2 pasará a "escuchar" el pipe1, quedando parado hasta que las clasificaciones del Programa1 terminen, comprobando a continuación el mensaje pasado por el pipe1 para decidir si los archivos están preparados para ser impresos , o si el programa debería terminarse. De esta forma es posible lanzar programas de forma sincronizada y no sincronizada cuando es necesario, ganando bastante tiempo de procesamiento.
Hits="$(cat page.hits 2> /dev/null)" || Hits=0
echo $((Hits=Hits++)) > page.hits
De esa forma si la página recibe dos o mas accesos concurrentes, uno o mas podrá(n) perderse, basta que el segundo acceso sea hecho después de una lectura del archivo page-hits y antes de su grabación, es decir, basta que el segundo acceso sea hecho después de que el primero haya ejecutado la primer línea del _script_y antes de ejecutar la segunda.
Entonces que hacemos? Para resolver el problema de concurrencia vamos a utilizar un named pipe. Creamos el siguiente scritp que será el daemon que recibirá la pagina en nuestro site que necesita de un contador.
echo "test_pagina.html" > /tmp/pipe_contador
Para evitar errores, en cada página que quisiéramos agregar el contador añadiriamos la siguiente linea:
<!--#exec cmd="echo $REQUEST_URI > /tmp/pipe_contador"-->
Observa que la variable $REQUEST_URI contiene el nombre del archivo que el navegador
(browser)pidió.
Este último ejemplo ,es fruto de una idea que intercambié con un amigo y maestro en Shell
, Thobias Salazar Trevisan que escribió el script y lo colocó en su excelente URL.
Aconsejo a todos que los que quieren aprender Shell que le echen un vistazo y la incluyan en sus favoritos.
Aja! Pensaste que el asunto sobre los named pipes estaba terminado? Pues estabas engañado. A continuación te voy a mostrar un uso diferente de ellos.
named pipes . Ahora te voy a mostrar que el Shell también usa los named pipes de una manera bastante singular, que es la sustitución de procesos (process substitution). Una sustitución de procesos ocurre cuando dentro de un pipeline de comando pones un comando entre paréntesis y un < o un > unido al paréntesis de la izquierda. Por ejemplo, tecleando el comando:
ls -l ejecutado en un subshell como es normal (por estar entre paréntesis), redireccionará la salida a un named pipe temporal, que el Shell crea, usa y luego elimina. Entonces el cat tendrá un nombre de archivo válido para leer (que será este named pipe y cuyo dispositivo lógico asociado es /dev/fd/63), y tendremos la misma salida que la generada por la del ls -l, pero dando uno o mas pasos de lo usual, y esto es mas costoso para el computador.
Como podremos constatar esto? Fácil .. Mira el siguiente comando:
named pipe.
Debes estar pensando que esto es una locura nerd , no? Entonces supongamos que tenes 2 directorios:
dir y dir.bkp , y deseas saber si los dos son iguales ( aquella vieja duda: estará mi backup actualizado? ). Basta comparar los dos archivos de los directorios con el comando cmp, haciendo:
while read arq
do
((i++)) # así no es necesario inicializar i
echo "$i: $arq"
done < <(ls)
echo "En el directório corriente (`pwd`) existen $i archivos"
Está bien, yo sé que existen otras formas de ejecutar la misma tarea. Usando el comando while. La forma mas común de resolver este problema sería:
ls | while read arq
do
((i++)) # así no es necesario inicializar i
echo "$i: $arq"
done
echo "En el directório corriente (`pwd`) existen $i archivos"
Cuando ejecutase el script, parecería que esta todo ok, sin embargo en el comando echo después del none, vas a ver que el valor de $i se perdió. Esto se debe al hecho de que esta variable esta siendo incrementada en un subshell creado por el pipe (|) y que termina con el comando done, llevándose con él todas las variables creadas en su interior y las alteraciones hechas en todas la variables, inclusive las creadas externamente.
Solamente para mostrarte que una variable creada fuera del subshell y alterada en su interior, pierde las alteraciones hechas al final, ejecuta el script siguiente:
#!/bin/bash
LIST="" # Creada en el shell principal
ls | while read FILE # Inicio del subshell
do
LIST="$FILE $LIST" # Alterada dentro del subshell
done # Fin del subshell
echo :$LIST:
Al final de la ejecución vas a ver que aparecerán apenas dos puntos (::). Pero en el inicio de este ejemplo te dije que era meramente didáctico, ya que existen formas mejores de hacer la misma tarea. Mira estas dos:
arq1 y arq2 usando el comando comm, pero este comando necesita que los archivos estén clasificados. Entonces la mejor forma de proceder es:
. Disfruté mucho aquí y recibí diversos elogios por los trabajos realizados a lo largo de 12 meses y, lo mejor de todo, hice muchas amistades y tomé muchos chopps gratis con los lectores que encontré por los congresos y charlas que ando haciendo por nuestro querido Brasíl.
Lo que voy a escribir aquí no está arreglado ni sé si será publicado, pero como los editores de esta revista son dos locos hermosos (ambos Rafael), es posible que lo dejen pasar. Es lo siguiente: si quieren que el Papo de Botequin continue, llenen la caja postal de la Linux Magazine pidiendo esto y desde ya escoja el próximo tema entre sed + expresiones regulares o lenguaje awk.
De cualquier forma, en caso de que no consigamos sensibilizar la dirección de la revista, me despido de todos mandando un abrazo a los barbudos y besos a las chicas y agradezco a los mas de 100 mails que recibí (todos elogiosos) y todos debidamente respondidos.
A la salud de todos nosotros: Chin, chin
-Chico, cierra la cuenta que voy a cambiar de bar.
Y no te olvides, cualquer duda o falta de compañia para tomar una cerveza o hasta para hablar mal de los políticos lo único que tienes que hacer es mandarme un e-mail para julio.neves@gmail.com. Voy aprovechar tambiém para mandar mi aviso publicitario: puedes decirle a los amigos que quien quiera hacer un curso nota diez de programación en Shell que mande un e-mail para julio.neves@uniriotec.br para informarse.
Gracias y hasta la próxima!
-- DanielRefosco - 3 Jan 2007
(CC) 2009 Pelos Frequentadores do Bar do Júlio Neves.