Etiquetas

martes, 17 de febrero de 2015

En los *nix, los pipelines son un caño

Parte de la "filosofía" de Unix (o de la programación para sistemas *nix) puede resumirse según el principio "escribí programas que hagan una cosa, y que la hagan bien".

Los pipelines, "pipes" son el mecanismo que permite extender este principio combinando y comunicando distintos procesos, para que cada uno haga su tarea, de la mejor forma posible.
En esta entrada, la historia de la noche febril en que Ken Thompson escribió la llamada al sistema pipe() y un par de breves y mundanos ejemplos de uso de pipes en bash.


Una febril noche de 1973 y una orgía de one-liners

Dice Wikipedia ( Pipeline_(Unix) ) que el concepto de pipeline (el paso de la salida de un programa a la entrada de otro) fue inventado por Douglas McIlroy, uno de los autores de los primeros shells. McIlroy notó que buena parte del tiempo estaban dirigiendo la salida de un programa a la entrada de otro.
Sus ideas las implementó Ken Thopson en una "noche febril" de 1973, cuando agregó la llamada al sistema pipe() y el mecanismo de "pipe" | a shell, junto con varias utilitarios en la versión 3 de Unix. Al día siguiente surgieron los "one-liners" (entradas de una líneas que encadenaban varios programas): dice McIlroy "vi una orgía inolvidable de one-liners en la medida que cada uno se sumaba a la diversión de meter todo en un caño".

Comunicar procesos en Bash

Los "pipes" o "pipelines" son una de las formas más simples y básicas de comunicar procesos (IPC: "Inter Process Communication"). Una de las grandes ventajas de los shells *nix es la posibilidad de crear one-liners que son combinaciones de programas, que juntos hacen una tarea en la que cada uno contribuye.
Solo un ejemplo, tomado de www.bashoneliners.com:

$ /usr/bin/printf 'GET / \n' | nc yahoo.com.ar 80

En este ejemplo se puede grabar el html de yahoo.com.ar usando netcat como cliente. Esa era la orgía a la que McIlroy se refería.

Existen pipelines con nombre y pipelines sin nombre, los primeros son básicamente un tipo de archivo especial en el File System al cual cierto/s proceso/s pueden escribir y otros leer. Los pipeline sin nombre son los que más usamos en la cli, cuando se usa para dirigir la salida de un comando a la entrada de otro con el caracter '|'.
Un pipe funciona, oh sorpresa, como un archivo y puede leerse de la misma forma.
Sigue abajo un ejemplo con el que me divertí un rato y con la esperanza que le pueda servir al sr. @esturniolo:

#!/usr/bin/env bash
#### Script for testing porpouses. Showing unnamed pipes in bash
#### imagemagick must be installed, so: # aptitude install imagemagick.


function printUsage {
    echo >&2 'Error: no argument supplied.'
    echo >&2 "Usage $0 "
    echo >&2 "$0 will start looking for image files starting at the initial directory you supply"
exit 1

}

function checkDep {
    type $1 > /dev/null 2>&1
    if [ "$?" -ne 0 ]
    then
     echo >&2 "Dependency $1 not met. Install it!"
    exit 1
    fi
}
function main {
    echo "inicial dir=" $1
    COMMAND="find $1 -iregex .*\.\(jpg\|gif\|png\|jpeg\)$ -type f"
    $COMMAND | while read i
    do
    # Here you do whatever you want with each lined passed throgh the pipe.
    # That's the unix magic.
      echo $i
      # in this test we grab some data from image file properties.
      identify -format "%wx%h %[EXIF:DateTime] " $i
    done
    echo
}

# Test user input: parameter $1, which must be the initial dir.
if [ "$#" -eq 0 ]; then
    printUsage
fi
if [ ! -d $1 ]; then
    echo >&2 "$1 is not a valid directory, so it's none of my bussiness fellow."
    exit 1
fi

# For checking dependency pass bin/script to be checked as checkDep parameter:
checkDep identify
main $1

A este script se le pasa como único parámetro un directorio inicial para que busque recursivamente en él archivos de imágenes:

find $1 -iregex .*\.\(jpg\|gif\|png\|jpeg\)$ -type f

Donde $1 es el primer parámetro que se le pasa al script. Ejemplo:

$ ./test_stu.sh /home/retux/fotos

La magia de pipe:

$COMMAND | while read i
do
    # Here you do whatever you want with each lined passed throgh the pipe.
     # That's the unix magic.
     echo $i
     # in this test we grab some data from image file properties.
     identify -format "%wx%h %[EXIF:DateTime] " $i
done

Cada línea que find en este caso devuelva por su stdout es redirigida por un pipe y almacenada en la var $i. Luego podemos hacer o que querramos, en este caso se llama al programa identify para que imprima algunos atributos de cada imagen, cuando se le pasa su ubicación, contenida en $i.

No hay comentarios.:

Publicar un comentario