17 de Marzo de 2010
Labels: bash, Debian, imagen, less, terminal.

El comando less de GNU es mucho más que un sustituto de more (es decir, un "paginador"). Lo cierto es que es sorprendentemente configurable, tanto que puede utilizarse para visualizar todo tipo de documentos, eso sí, convertidos a texto. A continuación, como ejemplo, describiré cómo configurarlo para ver imágenes en una consola.

La teoría

less tiene una funcionalidad llamada "preprocesado de la entrada" por la que se puede configurar un programa que lea el fichero que se va a visualizar y lo convierta en algo más apropiado para ser visto por el usuario. Esto se configura con las variables de entorno LESSOPEN y LESSCLOSE, que serán comandos que se van a ejecutar antes y después de que se presente el fichero en el terminal.

El ejemplo clásico es el siguiente, para poder ver archivos comprimidos con gzip:

$ export LESSOPEN="| $HOME/lessgz %s"
$ export LESSCLOSE=""

Siendo el fichero $HOME/lessgz:

#!/bin/bash
case "$1" in
  *.gz)
    zcat "$1"
    ;;
  *) #tipo de archivo desconocido
    exit 1 # se mostrará el archivo original
esac
exit 0

¡No olvides el permiso de ejecución en el fichero lessgz!. En el comando definido por LESSOPEN se sustituye el %s por el nombre del fichero de entrada. Si el primer carácter es un '|', entonces se debe escribir el texto visible en la salida estándar, en otro caso se debe guardar a un archivo temporal y escribir en la salida estándar el nombre de ese archivo. En este último caso resulta útil LESSCLOSE para borrar el archivo temporal.

Si utilizas un sistema basado en Debian (como Ubuntu), entonces lo más probable es que ya tengas instalado y configurado un preprocesador de less. Puedes comprobarlo escribiendo:

$ echo $LESSOPEN
| /usr/bin/lesspipe %s

El comando lesspipe entiende muchos formatos diferentes, aunque casi todos los transforma utilizando un programa externo, si está instalado. Entre otras entiende las siguiente extensiones: .z, .gz, .tar.gz, .zip, .doc y (mi favorito) .pdf. También entiende varios formatos de imágenes, pero las filtra usando el programa identify del paquete ImageMagick, que solamente muestra las propiedades básicas de la imagen (tamaño y colores). lesspipe permite al usuario añadir filtros personalizados creando un programa en ~/.lessfilter, que se llamará antes de nada. ./pre

La práctica

La idea básica es convertir la imagen en texto utilizando la herramienta pbmtoascii, disponible en el paquete netpbm. Este toma como entrada una imagen en formato pbm (un bitmap monocromo) y la convierte a caracteres. Pero como queremos ver imágenes de cualquier formato, la convertiremos a pbm utilizando el programa convert de ImageMagick.

El script resultante sería algo así (~/.lessfilter):

#!/bin/bash
case "$1" in
    *.gif|*.jpeg|*.jpg|*.pcd|*.png|*.tga|*.tiff|*.tif)
        convert "$1" pbm:- | pbmtoascii
        ;;
    *)
        exit 1
esac

Si tu sistema no está basado en Debian, quizás debas definir la variable LESSOPEN:

$ export LESSOPEN="| $HOME/.lessfilter %s"
$ export LESSCLOSE='' # solo por si acaso

Y no olvides los permisos de ejecución:

$ chmod a+x ~/.lessfilter

Redimensionando la imagen

Esto funciona muy bien, pero tiene un problema, y es que la mayoría de los terminales de texto no tienen más de unas 150 columnas, por lo que las imágenes de más de 150 píxeles de ancho no se van a ver muy bien. Sería bonito que redimensionara la imagen automáticamente al tamaño del terminal. Afortunadamente en bash existe la variable COLUMNS, cuyo valor es precisamente ese, el número de columnas del terminal. Pero no todo va a ser tan sencillo, esta variable (y su compañera LINES) solo están disponibles en shells interactivos, y un script, por su propia naturaleza, es no interactivo.

Solución #1. Si se cambia la primera línea del fichero por #!/bin/bash -i se fuerza a que el script se ejecute de forma interactiva. Funciona, pero no es muy elegante, porque se cargan también todos los scripts de arranque que asumen un entorno interactivo, y pueden escribir basura en pantalla.

Solución #2. Se añade la siguiente línea al script:

COLUMNS=`bash --noprofile --norc -i -c 'echo $COLUMNS'`

Pero, misteriosamente, no funciona. Resulta que la variable COLUMNS solo se asigna cuando se redimensiona el terminal (la señal SIGWINCH). A no ser que...

Solución final A no ser que se active la opción checkwinsize, en cuyo caso se obtiene el tamaño del terminal antes de cada comando. El script .lessfilter final queda así:

#!/bin/bash
COLUMNS=`bash --noprofile --norc -i -O checkwinsize -c 'echo $COLUMNS'`
case "$1" in
    *.gif|*.jpeg|*.jpg|*.pcd|*.png|*.tga|*.tiff|*.tif)
        convert "$1" -resize ${COLUMNS}x pbm:- | pbmtoascii
        ;;
    *)
        exit 1
esac
exit 0

Nótense las llaves en la referencia a la variable ${COLUMNS}. La razón por la que esto es necesario queda como ejercicio al lector ;-).

EDITADO: 2 de junio de 2010

Vaya, como suele pasar en esto de Linux, ya existe un comando que hace justo lo que necesitas. Lo difícil es conocerlo cuando lo necesitas. Resulta que stty size escribe en la salida estándar el tamaño del terminal, ¡con el trabajo que me había costado! Así .lessfilter acaba siendo lo siguiente (no es que sea más corto, pero es mucho más elegante):

#!/bin/bash
read FILES COLUMNS <<< `stty size`
case "$1" in
    *.gif|*.jpeg|*.jpg|*.pcd|*.png|*.tga|*.tiff|*.tif)
        convert "$1" -resize ${COLUMNS}x pbm:- | pbmtoascii
        ;;
    *)
        exit 1
esac
exit 0

NOTA: Por si alguien no lo conoce, la redirección <<< (solo en Bash) coloca el texto de la derecha en la entrada estándar del comando de la izquierda. Podrías sentirte tentado a hacer ssty size | read FILES COLUMNS, pero eso no funciona, porque el comando read se ejecuta en un subshell (un proceso nuevo), y las variables FILES y COLUMNS no son visibles en el intérprete actual.

0 comments to Ver imágenes con LESS

Help
:-(icon_sad :-)icon_smile :roll:icon_rolleyes
:-Dicon_biggrin :-Picon_razz :oops:icon_redface
:-xicon_mad :-|icon_neutral :arrow:icon_arrow
8-)icon_cool 8-Oicon_eek :mrgreen:icon_mrgreen
:!:icon_exclaim :?:icon_question :twisted:icon_twisted
;-)icon_wink :evil:icon_evil :idea:icon_idea
:-oicon_surprised :cry:icon_cry
:-?icon_confused :lol:icon_lol

Leave a comment