miércoles, 18 de febrero de 2009

Sutilezas de HAL

Sigamos haciendo pruebas con nuestro guión del día pasado.

Ahora, en lugar de pedirle a HAL que busque un término que consta de una sola palabra, le vamos a proporcionar uno que consta de dos:

./buscar_en_jargon "hacker ethic"

Si el lector ejecuta la orden anterior, sufrirá una temporal decepción, porque en lugar de ver el contenido del artículo hacker ethic, verá otra vez el del artículo hacker y una línea final desasosegante:

No definitions found for "ethic"

¿Cómo se explica este comportamiento? Contemplémoslo desde la perspectiva de HAL:

  1. Al llegar a su análisis de los argumentos de la orden, HAL se topó con unas comillas iniciales y guardó todo lo que fue encontrando por el camino en la variable $1 hasta alcanzar las comillas de cierre. O sea, que sí que reconoció las comillas. De hecho, si, en lugar de "hacker ethic" (con comillas) hubiésemos puesto hacker ethic (sin comillas), HAL habría guardado hacker en la variable $1 y ethic en la $2, es decir, habría tomado cada palabra por un argumento diferente. Pero sucedió también algo que no esperábamos ---y éste es el pero por el que la cosa falla---: lo que guardó HAL lo guardó sin las comillas, porque una vez que comprendió, gracias a nuestras comillas, que había un único argumento y no dos, se desentendió totalmente de ellas y se limitó a guardar en la variable $1 lo entrecomillado, es decir, hacker ethic, en lugar de "hacker ethic".

  2. Antes de ejecutar la primera línea de nuestro guión, HAL sustituyó la variable $1 por su valor (hacker ethic) y procedió, después, a ejecutarla ---las demás líneas, se recordará, estaban comentadas y, por tanto, eran invisibles para HAL.

  3. dict, por su parte, obró como debe hacerlo: nos devolvió el contenido del artículo hacker (su primer argumento) y un mensaje de error, puesto que no encontró ninguna entrada en el Jargon para el artículo ethic (su segundo argumento).

Está claro, pues, que debemos descubrir una forma de conseguir que el valor de $1 resulte entrecomillado antes de que HAL trate de ejecutar la orden contenida en el guión, de modo que, al sustituir la variable por su valor, HAL ejecute:

dict -d jargon "hacker ethic"

en lugar de lo que hizo antes:

dict -d jargon hacker ethic

Para este tipo de situaciones existe una triquiñuela sencilla, porque, una vez que se conoce el comportamiento descrito, es lo primero que a uno se le ocurre, a saber, entrecomillar el parámetro posicional:

"$1"

Gracias a esta argucia, podemos reeditar el guión una vez más y hacer que su primera línea sea ahora:

dict -d jargon "$1"

Hagamos un segundo intento.

./buscar_en_jargon "hacker ethic"

El lector comprobará que funciona a las mil maravillas, ya sin sustos de ninguna clase.

Bien, verificada la corrección de la primera línea de nuestro guión, toca descomentar las líneas anteriormente comentadas y volver a ejecutarlo. Dicho y hecho, nuestro guión debe ser ahora el siguiente:

dict -d jargon "$1" \
| grep -E -o '[{][^[:punct:]]+}' \
| sort \
| awk 'BEGIN {FS="\n"; print "hacker:"} { print " " $1 }' \
| tee -a jargon_refs

Probemos su funcionamiento completo:

./buscar_en_jargon "hacker ethic"

El resultado será:

hacker:
{FidoNet}
{GNU}
{gray hat}
{samurai}
{superuser}
{tiger team}
{Usenet}

Casi perfecto. Pero lamentablemente hay un fallito. En lugar de la palabra hacker, el título de la serie de referencias cruzadas debería ser hacker ethic. En esta ocasión el fallo no es, en modo alguno, achacable a HAL. Ya podemos dar gracias de que HAL no tenga nudillos y nos propine un capón por lo despistados que somos. Porque, claro ---ahora lo vemos---, se nos olvidó cambiar la cadena de caracteres que empotramos en el primer print de la orden awk dentro del guión, una rémora de nuestra prehistórica versión inicial:

awk 'BEGIN {FS="\n"; print "hacker:"} { print " " $1 }'

Por supuesto, en lugar de esa cadena tiene que haber también una variable, algo que luego HAL, cuando interprete el guión, sustituya por el primer argumento de nuestra orden ./buscar_en_jargon.

Y aquí viene el escollo. Sucede a menudo. Tenemos todo a punto menos un pequeño detalle, que es el que más difícil resulta de arreglar. ¡Vaya mala pata! Pero la paciencia es la madre de la ciencia y algo nuevo aprenderemos si deshacemos el actual embrollo. Que no es fácil de deshacer, resulta obvio a poco que se piense. No funcionaría en absoluto algo como esto:

awk 'BEGIN {FS="\n"; print $1 ":"} { print " " $1 }'

Y no por las comillas, sino por algo mucho más inmediato: awk usa ese mismo nombre de variable para una variable interna suya, a saber, la que contiene el valor del primer campo de cada registro del flujo de texto entrante, como se explicó hace algún tiempo. Es decir, dentro de awk la variable $1 tiene un significado diferente que el que esa misma variable tiene para HAL fuera de la instrucción awk.

Estaríamos en un callejón sin salida si awk no nos proporcionase un medio para poder acceder al valor de una variable ajena a las suyas propias. Por fortuna ---o más bien, por obra de sus concienzudos creadores---, awk dispone de un mecanismo que nos viene de perillas para salir del atolladero. Lo veremos el día que viene.


Resumen:

  • Un parámetro posicional pensado para contener un argumento que consta de varias palabras de una orden emitida desde la línea de órdenes se debe entrecomillar.

  • Una misma variable puede tener significados distintos dependiendo del contexto en que se encuentre. Normalmente, prevalece el significado del contexto más próximo.

No hay comentarios:

Publicar un comentario