Mostrando entradas con la etiqueta script. Mostrar todas las entradas
Mostrando entradas con la etiqueta script. Mostrar todas las entradas

martes, 12 de abril de 2011

Implementando un hotfolder en Linux

Una de las ventajas de la consola de UNIX, en general, y de Linux, en particular, es la facilidad de procesar por lotes multitud de ficheros. Eso está bien cuando tenemos todos los ficheros disponibles para ser procesados. Pero, ¿qué pasa cuando queremos montar un "hotfolder", es decir, un directorio donde los usuarios irán dejando sus ficheros para que vayan siendo procesados.


El reto principal consiste en saber cuándo el fichero se ha terminado de enviar, para pasar a procesarlo. Si lo enviáramos mediante un sistema construido a medida, se podría monitorizar el envío y generar un mensaje al finalizar, o implementar cualquier otro mecanismo.


En nuestro caso, queremos que los usuarios suban los ficheros por FTP, con lo cual no podemos añadir ningún mecanismo extra de control a la subida, teniendo que monitorizar el directorio por nuestros propios medios.


Buscando en Internet, hemos encontrados dos métodos que pueden ser útiles para lo que queremos conseguir:


Chequear el tamaño del fichero o si está abierto por algún proceso [1]. Podemos usar el comando fuser. Si el fichero no está en uso, no devolverá salida alguna, mientras que si lo está, nos dirá qué proceso lo está usando. En nuestro caso, el fichero estaría abierto por el servidor de FTP hasta que se complete la transferencia.


Monitorizar el uso de un fichero [2]. Mediante el uso de una utilidad como inotifywait, incluida en el paquete inotify-tools, podemos monitorizar, por ejemplo, cuándo se cierra un fichero en un directorio dado (también se puede hacer recursivamente) y, en el momento en que se produzca el evento, lanzar la ejecución de un proceso (en nuestro caso, el que procesa el fichero).


[1] http://www.unix.com/shell-programming-scripting/153036-check-if-file-finished-copy.html


[2] http://linux.die.net/man/1/inotifywait

jueves, 3 de marzo de 2011

Procesando ficheros

Ayer me surgió la necesidad de procesar un montón de ficheros, distribuidos por diferentes directorios. En concreto, tenía que generar un fichero JPEG a partir de un PDF.


Dividimos la tarea en dos partes: por un lado, extraer la lista de ficheros que queremos procesar. Por el otro, el comando que los procesa en sí.


El resultado sería algo como esto:



find . -type f -name "*.pdf" | while read i ; do convert -geometry 100x135 -quality 80 $i `dirname $i`/`basename $i .pdf`.jpg; done

La lista de ficheros la generamos a partir de un find: find . -type f -name "*.pdf" (suponiendo que estemos ubicados en el directorio a partir del que vamos a buscar los ficheros para procesar.


El bucle de procesado sería: while read i ; do COMANDO_PROCESADO; done.


En nuestro caso, el comando de procesado es la herramienta convert: convert -geometry 100x135 -quality 80 $i `dirname $i`/`basename $i .pdf`.jpg. Lo que hace es generar una miniatura en JPEG de 100x135px y 80 de calidad alojada en el mismo directorio que el PDF original.


Ahora imaginemos que lo que necesitamos es modificar la ruta, no el nombre del fichero. Lo podemos lograr usando sed:



find . -type f -name "*.jpeg" | while read i ; do echo convert -geometry 74x74 -quality 80 $i `echo $i | sed 's/RUTA1/RUTA2/g'`; done

En el ejemplo, sustituimos RUTA1 por RUTA2 en el nombre del fichero destino.

martes, 15 de febrero de 2011

Generar la salida de error en un script PHP


La salida de un script PHP lo normal es que sea en la salida estándar, que es donde se vuelca la información para que sea recogida por el cliente de turno. Esto suele hacerse mediante las sentencias echo y print que siempre imprimen en la salida estándar, pero ¿y si quiero enviar un mensaje a la salida de error?.


Cuando programamos nuestros scripts de linea de comandos surge la cuestión de necesitar trabajar con el resto de canales de entrada y salida que define el sistema. De los canales definidos en el sistema como salida tenemos dos, la salida estándar (stdout) y la salida de errores (stderr). Son dos canales distintos por donde se puede volcar la salida de un script, y por lo tanto nos puede interesar poder especificar porque canal quiero mandar la información. Lógicamente la salida estándar será para los mensajes de proceso y la de error para mensajes que indiquen un comportamiento anómalo al proceso.


Para esto PHP define una serie de secuencias de entrada y salida para poder utilizar con las funciones de tratamiento de ficheros, y que nos permite tratar información sobre los canales del sistema. Sobre los canales de salida que nos interesan están php://stdout y php://stderr que son los que ofrecen acceso al canal de salida estándar y de error respectivamente. Estos canales están definidos en unas constantes (STDOUT y STDERR) que definen el descriptor de cada canal, y que permiten trabajar con ellos usando directamente las funciones de escritura…



fwrite(STDOUT, "Conectando a servidor");

fwrite(STDERR, "ERROR: No se pudo acceder al recurso");


Al utilizar la constante STDERR como descriptor de fichero en la función de escritura, todos los mensajes que se escriban saldrán por el canal de error.


Así en nuestro scripts podremos diferenciar este canal…



php -q script.php 2> /var/log/script/script_php.error.log


Diferencias entre entornos


Para ejecutar un script de PHP podemos tener el intérprete compilado de dos formas diferentes: CGI y CLI


CLI


PHP actúa como si fuera un script de linea de comando. Es la versión que se tiene en el entorno de desarrollo, y sería la opción más adecuada para lanzar los scripts que se programan como tareas en el sistema.


No tiene asociada ninguna excepción sobre lo que hemos visto.


CGI


Integrado en los entornos de test y producción.


Usado antiguamente (y puede que actualmente) como el interprete que se configuraba en los servidores web para poder integrar PHP. Esta versión esta más orientada a ejecutar scripts en un entorno web, y no desde linea de comando como suelen ser nuestro caso. Es por esto que existen diferencias con la versión CLI y en este caso con los descriptores de ficheros.


No existen las constantes que representan los canales de salida STDOUT y STDERR, y por lo tanto hay que realizar la escritura de otra manera…



$fd_err = fopen("php://stderr","w"):

fwrite($fd_err, "ERROR: No se pudo acceder al recurso");


… para poder trabajar con la secuencia de error.


Solución


Para no tener que escribir código diferente entre las versiones se puede definir las constantes para que el trabajo sea igual y quede como si no hubiera diferencia…



if(!defined('STDERR')) define('STDERR', fopen("php://stderr","w"));

fwrite(STDERR, "ERROR: No se pudo acceder al recurso");


… así, si la constante no está definida se creara, y nuestro código quedaría igual para los diferentes entornos.

martes, 18 de enero de 2011

Cómo manejar directorios con muchos ficheros

Estaba configurando el script de PHPDoc y me he encontrado con la limitación de que no permite indicar ficheros con wildcards. Es decir, o le dices que escanee un directorio, y lo hará por completo (subdirectorios incluidos), o bien le indicas qué ficheros quieres que parsee, separados por comas (pero sin espacios).


Lo de sin espacios es importante, ya que ls -m no genera una salida válida para PHPDoc.


Así que tenemos que tirar de un poco de shell. La solución implementada, además, permite el manejo de directorios de cualquier número de ficheros (gracias al comando find):



find $DIR -maxdepth 1 -name "*.php" -exec echo -n "{}," \; | sed -e 's/,$//g'