domingo, 30 de junio de 2013

Descargando especiales rock sinfónico de Discópolis

Hoy voy a compartir un script bash que hice hace unos meses para automatizar las descargas de los programas especiales sobre rock progresivo y sinfónico que se han ido emitiendo en el espacio radiofónico Discópolis, de Radio 3. Por lo visto comenzó como una serie de 6 o 7 programas donde se pretendía hacer un resumen informal de los principales grupos que dieron lugar a este estilo musical en los años 70, pero la cosa se fue alargando (para deleite de los aficionados) y ya van más de 40 programas. Aprovechando su publicación en formato MP3 en la web de RTVE, pensé en hacer un script que automatizara la descarga para tener la colección completa y poder escucharla donde y cuando quiera. En principio debería funcionar en cualquier ordenador con Linux o Mac OS X, ya que ambos usan la shell Bash y he flexibilizado el uso del cliente HTTP para que use Wget (Linux) o cURL (Mac). Además no debería ser complicado adaptarlo para descargar otros programas de la misma web modificando la query string (principalmente los parámetros pageSize y ctx de la variable query) y las variables prog_pattern (el patrón de búsqueda) y prog_prefix (prefijo para nombrar los archivos). El código, listo para pegar en un archivo de texto, se puede ver aquí (no olvidar darle permisos de ejecución). That's all, folks!

viernes, 21 de junio de 2013

Funciones en un shell script: trackbin

Hace poco comentaba lo útil que podía resultar definir nuevas funciones en la shell y usarlas como si se tratara de nuevos comandos, nuestros propios comandos. El otro uso de las funciones shell, quizá más clásico, es dentro de scripts, como un recurso para que el programador organice su código. Cuando cierta secuencia de instrucciones (en este caso comandos) se utiliza repetidas veces a lo largo de un programa (en nuestro caso script) lo mejor es declararlo como una función que se invocará allá donde sea necesario, teniendo en cuenta ciertas cuestiones:
  • A una función se la invoca como a un comando o script, por su nombre y adjuntando si son necesarios los parámetros separados por espacios.
  • Dichos parámetros (para ser preciso argumentos, una vez dentro de la función), los recibirá en las variables especiales $1,$2, etc. solapando temporalmente las variables equivalentes donde el script recibe los parámetros pasados por el usuario desde la línea de comandos.
  • Si se necesita alguna variable local a la función, se puede usar la palabra reservada local antes de asignarle un valor por primera vez.
  • La función puede devolver con return un código de estado al igual que el script lo devuelve a la shell con exit. Éste se podrá usar para comprobar su éxito o no a modo de función que devuelve un valor booleano en lenguajes de programación tradicionales.
  • La forma de modificar y/o devolver otros datos más allá de ese valor de retorno, sería modificando variables globales (cualquiera definida en el script que no se haya "tapado" con una local del mismo nombre) o sacando datos a la salida estándar (un caso típico sería una función ayuda_de_uso()), que siempre podrían recogerse en el punto de la invocación dentro de una variable con la sustitución de comandos (mediante comando o $(comando) si no se quieren sacar aun por la salida éstandar.
Para ilustrarlo incluyo más abajo el código de un shell script llamado trackbin que rastrea los nombres de comandos hasta el archivo ejecutable último siguiendo los links intermedios:
#!/bin/bash

if [ -z "$1" ]; then
    echo "Usage: $(basename $0) BINARY_NAME"
    exit 1
fi

bin=$(which $1)

if [ -z "$bin" ]; then
    echo "$(basename $0): error: command '$1' not found"  
    exit 1
fi

function es_enlace {
    ls -l $(which $1) | grep -q ' -> '
    return $?
}

function donde_apunta {
    local destino=$(ls -l $(which $1) | awk '{print $NF}')
    if ! echo $destino | grep -q '/'; then
        destino=$(dirname $1)/$destino
    fi
    echo $destino
}

while es_enlace $bin
do
    bin=$(donde_apunta $bin)
done

echo $bin

exit 0