domingo, 15 de febrero de 2009

HAL y la senda

Del artículo anterior podemos extraer una simple idea con la que seguir adelante. Cuando escribimos un fichero ---como nuestro fichero prueba--- con la intención de que HAL lea de él las órdenes que contiene, estamos creando un fichero semejante a aquéllos en que consisten las órdenes que HAL nos proporciona de antemano, es decir, un fichero ejecutable (= una orden) y, particularmente, del mismo tipo que las órdenes escritas en el lenguaje interpretado que venimos denominando la "lengua de HAL".

Esta sola idea nos puede llevar a una conclusión casi completamente acertada, a saber, que para pedir a HAL que ejecute nuestro fichero prueba bastará con poner su nombre en la línea de órdenes.

Vamos a probarlo:

prueba

La respuesta es, desgraciadamente, ¡la misma de siempre!:

bash: prueba: command not found

¿Qué falla en nuestra deducción? Piense el lector unos segundos más y comprenderá que el hecho de que todas las demás órdenes conocidas se encuentren en directorios específicos (/bin, /sbin, etc.) puede tener que ver con el desesperante resultado.

Recordemos, de entrada, una de las conclusiones con las que se inició el artículo anterior y que reclama ahora una atención mayor:

... una de las primerísimas cosas que comprueba HAL cuando nos comunicamos con él es si la palabra introducida en la línea de órdenes corresponde o no al nombre de una orden existente en el sistema.

Nuestra palabra prueba, esto es, el nombre de fichero prueba, fue sometido a esta inicial verificación y descartado como orden porque la ruta del directorio que lo contiene no es ninguna de aquéllas en las que HAL entiende que deben estar los ficheros ejecutables, las órdenes. Y HAL, el muy pillín, conoce de antemano esas rutas, antes incluso de que iniciemos una conversación con él. ¿Cómo lo conoce? No es ningún misterio. Del mismo modo que obtiene sus otros conocimientos iniciales, por medio de una variable de entorno.

Recordaremos seguramente el tema de las variables de entorno en relación con nuestro esfuerzo por hacer que HAL respondiera en el idioma que deseábamos. Allí quedó determinado su sentido:

... desde el mismo momento que iniciamos nuestra charla con HAL, hay una serie de aspectos relativos a ella que han quedado establecidos de antemano y sin que nosotros hayamos sido conscientes. Los arquitectos de nuestra distribución son los responsables de haberlos definido por nosotros para nuestro propio beneficio, y el de HAL. Estos aspectos se denominan variables de entorno, porque son aspectos modificables del entorno de diálogo en que nos encontramos de entrada.

Pues bien, la variable de entorno donde se especifican las rutas de los ficheros ejecutables es la variable PATH (ruta o senda, en inglés). Podemos conocer su valor actual de varias formas. Por ejemplo, mediante la orden printenv, que, como su nombre indica (abreviatura de print environment) imprime en la salida el entorno actual. Por defecto, printenv devuelve todas las variables y sus valores actuales. Si se especifican como argumentos variables determinadas, devuelve el valor de esas variables.

Consultemos a HAL sobre el valor actual de la variable PATH:

printenv PATH

La respuesta de mi HAL es ésta:

/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games

Se trata de una lista de todas las rutas de los directorios donde residen órdenes ejecutables y donde cada ruta está separada de la siguiente por el signo ':'.

Se verá que faltan las rutas donde, según el artículo anterior, residen los ejecutables del sistema: /sbin y /usr/sbin. La razón es que sólo el superusuario puede hacer ejecutar estas órdenes del sistema y puesto que no somos el superusuario ---a menos que nos pongamos el disfraz de Superman y nos convirtamos en él--- nuestro entorno de usuario corriente no contiene tales rutas. El lector debería comprobar que hacer ejecutar la orden anterior (printenv PATH) con el disfraz de superusuario le dará otra respuesta, donde sí están incluidas las rutas de los ejecutables del sistema y donde no aparecerán, probablemente, otras indignas de la atención de root:

/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

Hay otra forma diferente de conocer el valor de la variable PATH, que no requiere el uso de una nueva orden y que nos viene bien para aprender un nuevo elemento de la lengua de HAL.

Como sabemos, la orden echo repite tal cual los argumentos que recibe. Pero existen excepciones a esa función suya de devolver sin ton ni son lo mismo que le damos. Una de estas excepciones tiene lugar cuando el argumento de echo es una variable con el prefijo $. Veamos el porqué.

Sabemos lo que es una variable, porque ya nos hemos topado con ellas, de hecho estamos ahora mismo hablando de una en concreto. Las variables ---incluidas las variables de entorno--- no son más que nombres asociados a ciertas informaciones. Por ejemplo, la variable LANG es el nombre que recibe en el entorno la información relativa a la lengua del sistema (inglés, español, japonés, etc.). A la información actualmente asociada con ese nombre se la denomina técnicamente el valor actual de esa variable, un valor que podemos cambiar cuando queramos (de ahí lo de variable). En nuestro caso, el valor actual de LANG es el español, pero podemos cambiarlo al japonés cuando queramos. Pues bien ---y siguiendo con el ejemplo--- $LANG, con el prefijo del dolar delante del nombre de la variable, es la forma que en lengua de HAL sirve justamente para designar el valor de la variable LANG.

Ahora bien, la orden echo, que recibe como argumento cualquier fragmento de texto, puede recibir también perfectamente el tipo de fragmento que sirve para designar el valor de una variable, por ejemplo, el vocablo $LANG. Cuando echo se encuentra con un argumento de esta clase, deja por un momento de comportarse de la manera simplona que lo hace ---reproducir tal cual lo que ha recibido--- y nos devuelve el valor de la variable cuyo nombre sucede al signo del dolar. Así:

echo $LANG

devuelve:

es_ES.UTF-8

y no simplemente ---como podría predecirse sin la anterior consideración---:

$LANG

En general, podemos saber el valor de cualquier variable y también de la variable que nos interesa en este artículo, la variable PATH, mediante la orden echo:

echo $PATH

Que, como era de esperar, nos vuelve a mostrar la salida obtenida con printenv PATH:

/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games

Si volvemos al comienzo de la digresión, nos resultará evidente por qué nuestra orden prueba, cuya ruta completa ---conviene recordarlo--- es /home/[nombre-usuario]/guiones/prueba no ha sido aceptada por HAL como una orden. Simplemente, el directorio /home/[nombre-usuario]/guiones no forma parte de la variable PATH.

Es evidente, también, que para obligar a HAL a que acepte nuestra orden prueba como tal orden y, en general, todos los guiones que a partir de ahora escribamos para él no queda más remedio que modificar la variable PATH para que incluya la ruta del directorio /home/[nombre-usuario]/guiones.

Sabemos modificar variables de entorno, lo hicimos hace tiempo con las variables del entorno local [véanse los artículos El supermanual de HAL y HAL y mis ficheros]. Probemos, pues, a pedir a HAL que ejecute nuestra orden prueba, pero con el PATH previamente modificado, tal como hicimos en el primero de los artículos referidos:

PATH=/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games:/home/atopos/guiones \
prueba

Antes de pulsar Enter consideremos tres aspectos de esta orden. El primero es obvio: la última ruta incluida en el PATH contiene el nombre del usuario después de /home. Mi nombre de usuario es atopos; el del lector será el que sea. El segundo también es obvio: todas las rutas están separadas por ':' ---no lo olvide el lector. El tercero es el más interesante: se han de incluir las rutas ya existentes en la variable PATH antes de cambiarla, de lo contrario, todos las rutas predefinidas de las órdenes habituales serían invisibles para HAL y HAL dejaría de reconocer como órdenes a nuestros viejos amigos echo, sed, grep, awk y compañía.

Esta última consideración puede llevar a algún lector a una deducción inteligente. Si la variable PATH ya tiene un valor antes de nuestro cambio ---un valor que, como hemos visto, se designa con $PATH--- y este valor debe ser preservado, ¿por qué no añadir directamente la ruta de nuestro directorio guiones a ese valor previo? Dicho más claramente, ¿por qué no poner en lugar de la larga expresión anterior esta otra?

PATH=$PATH:/home/atopos/guiones

Si el lector ha llegado a tal conclusión por sí mismo, ¡enhorabuena! Demuestra una inteligencia despierta y creativa y una comprensión plena de lo dicho en este artículo. Efectivamente, ésa es la forma habitual de modificar variables cuyo valor anterior debe mantenerse. Por tanto, nuestra forma de pedir a HAL que ejecute prueba debe ser la siguiente:

PATH=$PATH:/home/atopos/guiones prueba

Y, ahora sí ---por fin--- la respuesta de HAL cambia, pero de una forma totalmente inesperada:

bash: /home/atopos/guiones/prueba: Permiso denegado

Increíble, pero cierto. Incluso estando en nuestro home particular, HAL nos dice que no tenemos permisos. ¡Esto sí que es un galimatías!

Pero no, lector, HAL no ha dejado de funcionar cuerdamente. La razón de su comportamiento es muy significativa y será tratada en el próximo artículo.

Antes de ello, conviene comentar tres cosas más en relación con el asunto de este artículo.

La primera es que se podría editar nuestro fichero .bashrc para que el cambio de la variable PATH fuera permanente, de modo semejante a como se hizo en HAL y mis ficheros con las variables del entorno local.

La segunda es que podríamos incluir nuestros propios guiones en los directorios que el sistema tiene reservados para los binarios ejecutables. Algo muy poco recomendable, porque, aunque ello evita tocar la variable PATH, acaba mezclando órdenes del sistema con órdenes de usuario y las consecuencias de esta mezcolanza pueden ser bastante penosas, aparte de poco estéticas.

La tercera y última es que existe otra forma sencilla para conseguir que HAL entienda mis guiones como ficheros ejecutables sin necesidad de redefinir la variable PATH ni de añadir nada ajeno a los directorios del sistema. A saber, dar como orden la ruta completa del fichero que contiene la orden. En el caso de nuestro fichero prueba sería:

/home/atopos/guiones/prueba

Como esta opción es un tanto molesta, por larga, existe un atajo o, más exactamente, una abreviatura: hacer preceder la orden del caso con los signos punto y barra: './'. Es decir, que sin hacer ninguna modificación en la variable PATH y dando por sobrentendido que estamos dentro del directorio guiones podríamos escribir:

./prueba

Tanto la versión que incluye la ruta completa como esta abreviada (donde el punto es la abreviatura del directorio actual de trabajo) obtendríamos el mismo resultado inesperado de la orden anterior en que la variable PATH fue modificada.

De hecho, y en próximos artículos, utilizaremos este último recurso (./mi-orden) con el fin de diferenciar nuestras órdenes de las que proporciona el sistema.


Resumen

  • Cuando escribimos una orden en la línea de órdenes, HAL comprueba si el fichero que corresponde a esa orden existe en el sistema. Lo hace consultando los directorios definidos en la variable de entorno PATH.

  • La orden printenv (print environment) devuelve el valor actual de las variables de entorno que se le especifican como argumentos. Por defecto, esto es, sin argumentos, devuelve todas las variables del entorno y sus valores actuales.

  • Una variable es un nombre para un determinado fragmento de información.

  • El valor de una variable se designa mediante el nombre de esa variable precedido por el prefijo '$'. Por ejemplo, $PATH designa el valor de la variable PATH.

  • Cuando el argumento de echo es la designación de un valor de variable, echo devuelve el valor de esa variable. Por ejemplo, echo $PATH devuelve el valor de la variable PATH.

  • La combinación de signos './' como prefijo de un nombre de fichero, hace que ese nombre sea interpretado como el nombre de una orden existente en el directorio de trabajo actual. (El punto está ahí como abreviatura de la ruta completa de dicho directorio).

No hay comentarios:

Publicar un comentario