lunes, 30 de mayo de 2011

Cogiendo N líneas aleatorías sin repetición de un archivo

Se me presentó el siguiente problema: tenía un archivo con N líneas y debía tomar/coger de éste M líneas no repetidas y de forma aleatoria. Siendo M < N. Como me "da nota"/mola bash, decidí hacerlo con un script. Antes explico el algoritmo, inspirado en un método para barajar cartas:

  1. Creando el mazo de cartas. Creo un arreglo con tantas posiciones como líneas tiene el archivo de entrada (el de N líneas).

  2. Barajando el mazo de cartas. Cojo el primer y último elemento del arreglo origen (source) y los coloco de últimos en el arreglo destino (tmp). Esto lo hago hasta vaciar el arreglo de origen y repito el procedimiento un número "aleatorio" de veces, entre 20 y 120 veces en este ejemplo. Siempre reemplazando al final de cada bucle el arreglo de origen por el temporal.

  3. Intercambiando última y primera carta. Esto para garantizar que la primera carta no permanezca siempre igual

  4. Sacando M cartas del mazo. Cojo las primeras M cartas del mazo.


#!/bin/bash

# Variables
fileName=$1
inputLines=`wc -l $fileName | cut -d' ' -f1`
outputLines=$2 # 596 

# Filling array
for i in `seq 0 $((inputLines-1))`; do
  source[$i]=$((i+1))
done

# Shuffling array
ceil=$((RANDOM%10*10+20)) # from 20 to 120, increment 10

for rand in `seq 1 $ceil`; do

  tmpIndex=0
  for srcIndex in `seq 0 $(($inputLines/2))`; do
    let srcTopIndex=$inputLines-$srcIndex

    tmp[$tmpIndex]=${source[$srcIndex]}
    tmp[$((tmpIndex+1))]=${source[$srcTopIndex]}

    let tmpIndex+=2

  done

  # for arrays with odd length
  if [ $(($inputLines%2)) -eq 1 ]; then
    tmp[$tmpIndex]=${source[$((srcIndex+1))]}
  fi

  source=( ${tmp[@]} ) # copying tmp array
done

# Changing first element for last one
tmp=source[0]
source[0]=${source[$((inputLines-1))]}
source[$((inputLines-1))]=$tmp

# Printing random lines of input file
for i in `seq 0 $(($outputLines-1))`; do
  sed -n "${source[$i]}p" $fileName
done

Cosas interesantes en este ejemplo:
  • Manejo de arreglos: ver iteraciones, copia, extracción y asignación de valores.
  • Extracción de una línea específica de un archivo, basado en el número de línea con sed.


sábado, 14 de mayo de 2011

Apagando y encendiendo el mousepad del laptop

Hoy estuve peleando con mi laptop tratando de encender y apagar el mousepad, básicamente porque no me siento cómodo utilizándolo, para lo que conecto un mouse externo, y además porque lo tropiezo al teclear, lo que se vuelve algo desesperante.

Intenté "apagar" el mousepad desde el menú de configuración del ratón, pero no conseguí ninguna opción. Entonces tuve que descubrir qué nombre tenía mi ratón, para lo que utilicé el siguiente comando:

rodolfo@tiuna:~$ xinput list --short | grep -i 'touchpad'
⎜   ↳ SynPS/2 Synaptics TouchPad               id=12 [slave  pointer  (2)]

Y luego para encender y apagar el ratón fácilmente creé el siguiente script:

#!/bin/bash

if [ $# -gt 0 ] && [ $1 == "on" ]; then
 mode=1
elif [ $# -gt 0 ] && [ $1 == "off" ]; then
 mode=0
else
 echo "You must specify an option, on or off"
 echo "Usage: script.sh on"
 exit
fi

format=8

# xinput list --short | grep -i 'touchpad'
DISPLAY=:0 xinput set-int-prop 'SynPS/2 Synaptics TouchPad' 'Device Enabled' $format $mode

Entonces, por ejemplo para apagar el mousepad bastaría con hacer:
rodolfo@tiuna:~$ ./mousepad.sh off

Por cierto, tengo instalado Ubuntu 10.10, por lo que supongo que esto funcionaría como mínimo en todas las distribuciones hijas de Debian.



lunes, 9 de mayo de 2011

Particionando el disco de mi nueva máquina

Compré una máquina este fin de semana y para instalar Linux tuve que enfrentarme al típico problema de particionamiento del disco.

Siempre había hecho esto con Partition Magic, ahora de Symantec, pero esta vez tuvo que ser un poco diferente.

La cuestión es que la máquina vino con Windows 7 de fábrica y quería conservarlo. La última versión pirata que conseguí del programa, siento aceptar que instalé la pirata, me estaba dando varios problemas con este Sistema Operativo.

Pues me puse a buscar y descubrí una herramienta, seguro recontra conocida, pero totalmente desconocida por mi, Parted y su equivalente gráfico Gparted.

Fue tan fácil particionar el disco que dudé hasta el final... Inicié la máquina por DVD (tengo un Ubuntu 10.10 en DVD), inicialmente ejecuté "sudo gparted", pero luego me di cuenta de que estaba en el menú de Administración.

Borré las particiones de HP UTILS y RECOVER, para lo que previamente compré a través de la página de HP el DVD de Recovery, mero romanticismo de conservar la máquina tal cual la compré en algún futuro improbable. Y luego, instalar Ubuntu! Fáchil perolito!

Enjoy! Recuerden, Gparted es la herramienta.

martes, 12 de abril de 2011

Ubuntu Cosillas: Xvidcap grabaciones de escritorio (una alternativa muy recomendable)

Excelente!! Esto lo conseguí en el blog citado abajo, los pasos son (textual):

  • Uninstall xvidcap in Synaptic
  • Install xvidcap from http://sourceforge.net/projects/xvidcap/files/xvidcap/1.1.7/xvidcap_1.1.7jaunty_i386.deb/download
  • Lock the installed version in Synaptic
  • Install pavucontrol in Synaptic (Needed in Ubuntu 10.04 but not in Linux Mint 10: already present)
  • Open the program with padsp xvidcap
  • Click on the recording button (red circle)
  • Run pavucontrol, go to the Recording tab and there choose Monitor of Analog Stereo Internal Audio

Ubuntu Cosillas: Xvidcap grabaciones de escritorio (una alternativa muy recomendable)

domingo, 10 de abril de 2011

Barajita premiada (#24) - generando documentos PDF a partir de imágenes

Por cosas de la vida necesitaba entregar un PDF con mis notas de grado escaneadas, el problema: tenía un montón de imágenes y llevarlas a un PDF con Openoffice siempre quedaba mal por inconsistencias con los márgenes. La solución:

$ ls 
notas1.jpg  notas3.jpg  notas5.jpg
notas2.jpg  notas4.jpg  notas6.jpg
$ sudo aptitude install imagemagick
$ convert *.jpg notas.pdf

Listo! El paquete imagemagick trae el programa convert que permite manipular imágenes (formatos, tamaños, etc), éste cogió los archivos en orden (por la numeración) y generó el PDF en un pis-pas.


viernes, 8 de abril de 2011

Iterando sobre un archivo con claves y consultando en BD (en bash)

El problema: un archivo/fichero con columnas separadas por tuberías "|" y por cada identificador contenido en la primera columna de éste, se debía buscar en BD unos datos específicos.

Les dejo este pequeño script que tiene algunas cosillas útiles, como la utilización de contadores en bash fácilmente (a lo C).

#!/bin/bash

dbuser=DB_USER
dbhost=DB_HOST
dbname=DB_NAME
ln=1 # counter
sql="SELECT ... FROM ... WHERE ... id="

cat content.csv |
while read line; do
  id=`cut -d'|' -f1 <(echo $line)`

  if [ $ln -gt 1 ]; then
    mysql -u $dbuser -h $dbhost $dbname --batch --skip-column-names -e "$sql$id" | sed 's/\t/|/g'
  fi

  let ln+=1
done
Por cada línea del archivo content.csv se toma la primera columna (como id - luego de la primera línea) y luego se ejecuta una query en BD utilizando este número; el resultado es formateado con columnas separadas por tuberías. Interesante: la forma en que se incrementa el contado con "let+=1".
OJO: Esto se pudo hacer con otro lenguaje de programación, ideal perl, pero como me da nota/mola bash lo hice de esta forma. Además, cabe acotar que esto es sólo algo puntual, no es una base de código que deberá mantenerse en el tiempo, de no ser así, recomendaría otra vía.

martes, 29 de marzo de 2011

Barajita premiada (#23) - variables vacías o sin definir en bash

Recientemente tuve que hacer un script de bash, que entre tantas cosas, en un momento determinado debía validar y diferenciar si una variable existía o no tenía valor (era vacía).

Para resolver el problema descubrí dos operadores que desconocía...

#!/bin/bash

# a equals empty string (var instance exists)
a=""
echo "${a-NO_INSTANCE}"
echo "${a:-NO_VALUE}"

# a equals full string (var instance exists)
a="hello"
echo "${a-NO_INSTANCE}"
echo "${a:-NO_VALUE}"

# b not exists as var
echo "${b-NO_INSTANCE}"
echo "${b:-NO_VALUE}"


Al ejecutar el script verá una salida como la siguiente:

$ ./checkBashVars.sh 

NO_VALUE
hello
hello
NO_INSTANCE
NO_VALUE

En la salida mostrada arriba se puede ver que:
  • El operador "-": devuelve el valor de la variable (VAR) si existe, no importa si es vacía. En caso de no existir, devuelve el valor alternativo (VALOR).
  • El operador ":-": devuelve el valor de la variable (VAR) si existe y tiene valor (no es vacía). En caso de no existir, devuelve el valor alternativo (VALOR).

Un ejemplo para mostrar dónde se puede utilizar esto: imaginemos que tenemos un documento con identificadores, otro con registros (un CSV) donde estos identificadores están presentes y la idea es conocer cuál de los identificadores no está en el documento.

Identificadores:
$ cat ids
1
2
3

Fuente, CSV:
$ cat source
1|nice|bad
2|welcome|bye
1|tokio|caracas

Script que retornará los ids que no están presentes en el documento fuente (CSV):
#!/bin/bash

cat ids |
while read id; do
        match="`grep $id'|' $source`"

        if [ "${match:-NULL}" = "NULL" ]; then
                echo $id 
        fi
done

La salida:
$ ./script.sh 
1

El script mostrado arriba, busca dentro del archivo fuente (source) cada uno de los id contenidos en el archivo de ids y en el momento en que no consigue registros, porque el grep sobre el archivo retorna "NULL", imprime el id. Puede ver que el NULL es impreso utilizando el operador :-