jueves, 7 de abril de 2011

Consultando mysql desde bash

No es muy común que necesitemos consultar bases de datos mysql desde un shellscript, pero en ciertas ocasiones (por ejemplo en un cron), podemos desear hacerlo. Ya sea para registrar un backup en una tabla de logs o tomar algún dato necesario para nuestro script desde una tabla, puede suceder que desde bash necesitemos consultar a mysql sin querer usar un script intermediario hecho en otro lenguaje, como perl o php.


¿Cómo accedemos?


Para acceder a la base de datos utilizaremos el CLI de mysql y lo iniciaremos con dos parámetros:

  • -B Hace que el cliente CLI de mysql trabaje en modo batch entonces, en lugar de devolver los datos en tablas dibujadas en ascii, devolverá los resultados separando los campos por tabuladores y los registros por nuevas líneas. Además no muestra prompt.

  • -N Evita que el CLI de mysql devuelva los headers con los nombres de los campos



Ejemplo



mysql -N -B

Nota:Me centraré en las consultas select, las consultas de modificación se ejecutan fácilmente utilizando el parámetro -e de mysql CLI y especificando la consulta de modificación.

Luego debemos asegurarnos que el CLI de mysql reciba nuestras consultas, pero como no las tipearemos nosotros en el prompt, tenemos que redirigir nuestras consultas a su entrada estándar para que las reciba, esto se puede hacer fácilmente mediante echo y haciendo entubamiento hacia mysql. Además le indico a mysql el nombre de la base de datos en la que debe consultar.



Ejemplo



echo "SELECT * FROM mi_tabla WHERE 1" | mysql -N -B mi_base_de_datos

El siguiente paso es hacer que el resultado quede guardado en una variable, para esto utilizamos las comillas invertidas.



Ejemplo



RESULTADOS=`echo "SELECT * FROM mi_tabla WHERE 1" | mysql -N -B mi_base_de_datos`

Ahora ya tenemos los resultados de nuestra consulta en una variable y lo único que debemos hacer es parsearlos. Para esto debemos hacer uso de la variable especial IFS de bash, la cual indica el separador que se utiliza para delimitar tokens al usar programas como for o awk, seteándole qué caracter debe usar para dividir las cadenas. En nuestro caso '\n' para separar registros y '\t' para separar campos.


En este post intentaré hacer todo lo más simplificado posible para que sea claro y entendible, por lo que indicaremos en la consulta qué campos deseamos de la tabla para saber su orden.



El ejemplo completo


Los comentarios explican lo que hace el script.

DBUSER=mi_user
DBPASS=mi_pass
RES=`echo "SELECT id, nombre FROM mi_tabla WHERE 1;" | mysql -N -B -u $DBUSER -p$DBPASS mi_db` # Ejecuto la consulta y la guardo en RES
OLDIFS="$IFS" # Respaldo el valor de IFS, no olvidar las comillas dobles
IFS=$'\n' # Seteo el valor de IFS que me sirve
for row in $RES ; # Itero por los registros porque for separará por \n
do
if [ ${#row} -gt 0 ]; then # Chequeo si tengo un registro
INNERIFS="$IFS" # Hago un respaldo temporal de IFS, no olvidar las comillas dobles
IFS="$OLDIFS" # Ahora separaré los campos

id=$(echo $row | awk '{print $1}') # Utilizo awk porque sé el orden de los campos: id es el 1 y nombre es el 2
nombre=$(echo $row | awk '{print $2}')

echo "El id es $id y el nombre es $nombre"
IFS="$INNERIFS" # Vuelvo a hacer que IFS separe por \n para la próxima iteración
fi
done

IFS="$OLDIFS" # Dejo IFS en su estado original.

Como se ve es bastante fácil acceder a los datos, utilizando este esquema se pueden parsear resultados aun más complejos, es cuestión de experimentar un poco.

sábado, 2 de abril de 2011

Javascript: Usar métodos de clases con setInterval y setTimeout

El objeto Window de Javascript posee dos métodos que nos permiten lanzar una acción luego de cierto tiempo:



  • setTimeout lanza la acción una vez luego del tiempo especificado

  • setInterval lanza la acción en intervalos regulares de duración especificada


Por ejemplo:



<script type="text/javascript">
setTimeout('alert("Hola mundo")'. 5000); // Muestra la alerta hola mundo luego de 5000 milisegundos (5 segundos)
setTimeout('alert("Hola mundo")'. 3000); // Muestra la alerta hola mundo cada 3000 milisegundos (3 segundos)
</script>


Pero un problema clásico que sucede cuando trabajamos con clases en Javascript es podemos querer usar setInterval o setTimeout en un método de nuestra clase que ejecute como acción un llamado a un método de su misma instancia. Una primera solución que se nos ocurriría podría ser esta:


<script type="text/javascript">
function MiClase()
{
this.miIntervalo = null;
}

MiClase.prototype.miMetodo = function()
{
alert("Hola Mundo");
}

MiClase.prototype.iniciarIntervalo = function()
{
this.intervalo = setInterval('this.miMetodo()', 3000); // Esto no funcionará: utilizo 'this' (local) en el global scope
}

var miInstancia = new MiClase();
miInstancia.iniciarIntervalo();
</script>


Sin embargo el código anterior no funcionará, generando un error.


¿Por qué?

Como setTimeout y setInterval son métodos de la clase window, ejecutan sus acciones en el scope global, pero 'this' no pertenece al scope global, sino que es local a la instancia de la clase. Al tratar de ejecutar la acción setInterval no encontrará el objeto llamado this y devolverá un error.




Una solución


Una forma de solucionar esto es mantener una colección de objetos por fuera de la clase y asignar a cada instancia una key, en el constructor hacemos que el objeto se guarde a sí mismo en la colección y luego, cuando le indiquemos a setInterval o setTimeout cuál es la acción a ejecutar, utilizamos la colección en lugar de 'this'



Ejemplo de la solución



<script type="text/javascript">
var objetosMiClase = new Object(); // Mi colección de instancias de MiClase

function MiClase()
{
this.miIntervalo = null;
var date = new Date();
this.key = date.getTime() + date.getMilliseconds(); // Genero una key, se pueden usar otras técnicas
objetosMiClase[this.key] = this; // La instancia se guarda a sí misma en la colección
}

MiClase.prototype.miMetodo = function()
{
alert("Hola Mundo");
}

MiClase.prototype.iniciarIntervalo = function()
{
this.intervalo = setInterval('objetosMiClase[' + this.key + '].miMetodo()', 3000); // Funciona! utilizo un objeto del global scope en lugar de 'this'
}

var miInstancia = new MiClase();
miInstancia.iniciarIntervalo();
</script>


Esta es una solución rápida al problema de intentar llamar un objeto de la misma instancia utilizanso setInterval o setTimeout

Epistemomaniáticos ©Template Blogger Green by Dicas Blogger.

TOPO