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
#### 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
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.
Continuar »