Como en los lenguajes de programación corrientes, en la programación en shell existen las variables, que son nombres simbólicos para zonas de memoria que almacenan datos que nos interesan. Pero al contrario que los lenguajes de alto nivel normales, las variables de los guiones no tienen tipo, o quizás sería más apropiado decir que tienen un tipo único y permanente: a todos los efectos se tratan como ristras de caracteres.
Para poder hacer sumas y restas hay una orden especial, la orden
expr
. Esta orden tiene que recibir cada número u operando
como un parámetro independiente, así que devolverá un
error si intentas hacer algo como expr 5+7
. No vale la pena
extenderse aquí, aunque si quieres más información sobre
esta instrucción o cualquier otra característica de la
programación en shell, ya sabes adonde acudir.
Las variables de los guiones no se declaran, y siempre están inicializadas con una ristra vacía o nula. Nótese que esto no es lo mismo que contener un espacio. Una ristra vacía o nula es una ristra que no contiene ningún caracter.
Hay que tener cuidado al asignar valores a las variables, ya que no se debe dejar ningún espacio entre el signo de asignación (el `=') y la variable o el valor asignado (algo que, por otra parte, es una muy buena costumbre cuando da igual).
Para referirse a las variables, hay que utilizar el signo dólar ($) antes del nombre de ésta, siempre que nos refiramos a ellas para consultar su valor (si asignamos un valor a la variable, o utilizamos la orden read, que escribe en ella, NO hay que poner el signo de dólar). Si nos olvidamos del signo dólar, y hacemos algo parecido a:
y=hola
x=y
Nos encontraremos con la desagradable sorpresa de que el valor de x es
el carácter y
, y no los caracteres hola
, como
quizás pretendiéramos. Para hacer la asignación
correctamente, tendríamos que haber escrito:
y=hola
x=$y
Como en todas o al menos la mayoría de las cosas en UNIX, los
nombres son case sensitive, es decir, que no es lo mismo y
que Y
(¡aviso para los programadores acostumbrados al MS-DOS!).
Es útil saber el funcionamiento del analizador sintáctico (parser) del intérprete de órdenes a la hora de programar. Al fin y al cabo, significa saber cómo va a interpretar nuestras órdenes.
Como la mayoría de los analizadores de los lenguajes interpretados, primero realiza algunas sustituciones, al leer la orden, y luego ejecuta la orden que le queda después de las sustituciones.
Las sustituciones hechas por el intérprete de órdenes de
UNIX son sustituciones de comillas y sustituciones de variables (lo que vea
con un signo dólar que no esté ``escapado'').
Según el intérprete en particular, hará algunas
sustituciones más, como sustituciones del caracter ~
(que
indica el directorio raíz del usuario), los caracteres {}
, los
caracteres []
, etc.
Las sustituciones de variables son lo que más nos interesa.
Cuando el analizador se encuentra un singo de dólar, que no tenga antes
una barra invertida (\
), lo interpreta como una variable. Lo que
hace, entonces, es avanzar hasta que tiene el nombre completo de la variable,
y cuando lo tiene, sustituye el dólar y el nombre por el valor de la
variable. Si no existe ninguna variable que tenga ese nombre, no es
un error. Simplemente lo sustituye por nada. Este comportamiento puede
acarrear algunos fallos difíciles de descubrir. Por ejemplo, al
procesar la línea
mensaje="$foo y alguna otra cosa"
El analizador dectecta el signo $
, que indica variable,
avanza hasta que tiene completo el nombre de la variable (el nombre es, por
supuesto foo
), y sustituye la ristra $foo
por el contenido
de la variable en cuestión. Es importante tener esto en cuenta, porque
si quisiésemos poner bar
después del contenido de la
variable $foo
, no podríamos escribir $foobar
, porque
el analizador creería que estaríamos consultando la variable
foobar
. Lo que tenemos que hacer es ``delimitar'' el nombre de la
variable con llaves, quedando ${foo}bar
.
echo
y read
Dos órdenes bastante útiles para probar algunas
estupideces con variables son echo
y read
. La primera es
como su homónimo de MS-DOS (o mejor dicho: el de MS-DOS es una copia,
probablemente peor, de su homónimo UNIX), para mostrar texto por
pantalla, y la segunda, como su nombre indica, es para leer del teclado y poner
el resultado en una o varias variables. El funcionamiento de read
es el
siguiente: lee del teclado una ristra, y va asignando las palabras en orden a
cada una de las variables que se le pasen como parámetros, asignando el
resto de la ristra a la última variable. Es decir, que si se le pasan
tres variables como parámetro, se asigna la primera palabra a la primera
variable, la segunda a la segunda variable, y el resto de la ristra
leída a la tercera variable. Por ejemplo:
$ read foo bar
Escribo demasiadas palabras
$ echo $foo
Escribo
$ echo $bar
demasiadas palabras
Los parámetros son variables normales, que tienen los nombres
$1
, $2
... $9
. Aunque se pueden dar más de
nueve parámetros a un guión para el intérprete de
órdenes, sólo se puede acceder de forma directa a los nueve
primeros. La orden shift
permite desplazar los parámetros de
sitio, de tal forma que sean accesibles los que estén más
allá del noveno, con el inconveniente de no poder acceder a los
primeros. En realidad, en el Korn shell (y en bash
) se puede acceder
al resto de los parámetros directamente con la construcción
${número}
. El funcionamiento de shift
es el
siguiente:
Supongamos que tenemos como parámetros $1=-o
,
$2=foo
y bar
, por llamar al guión (suponiendo que el
nombre del guión es compila
) así:
compila -o foo bar
Lo que queremos es quitarnos de enmedio las opciones, después
de haberlas procesado, de tal forma que el tercer parámetro
(bar
) se quede como primero. Lo que haremos, entonces, es llamar dos
veces a shift
, o llamar a shift
con el parámetro 2.
Teniendo este código:
shift 2
echo $1
Y suponiendo la llamada anterior, el resultado por pantalla
sería bar
.
Las variables $#
, $*
, $0
nos permiten saber
el número de parámetros pasados al guión, la ristra
entera de todos los parámetros pasados, y el nombre del programa que se
ha llamado. Esto último puede parecer estúpido, pero piensa que
en UNIX pueden hacerse enlaces a ficheros. Si utilizas Linux, cuando llamas al
vi
en realidad estás ejecutando el fichero ejecutable
vim
. Fíjate cuando hagas un ls -l
en tu directorio
/usr/bin
. Tendrá que salir un fichero parecido a este:
rwxr-xr-x root root vi->vim
Eso significa que el fichero vi
no es un ejecutable, sino un
enlace que apunta al fichero vim
del mismo directorio.
Así, por ejemplo, hay un caso parecido con awk
, que apunta a
gawk
. Cuando llamas a cualquiera de los dos programas sin
parámetros, te da la ayuda, diciéndote cada uno de ellos que se
llama con el nombre con el que tú le hayas llamado (con esto no quiero
decir que el awk/gawk sea un guión, por supuesto, pero los lenguajes de
alto nivel tienen una opción parecida).
exit
Los valores devueltos por los programas, por convenio, son 0 si el programa ha ido perfectamente (cero porque es el código de error), y un número distinto de cero, normalmente 1 o -1 para indicar algún tipo de error. Es de notar que cuando ejecutemos un pipe, el valor devuelto por el pipe será el del programa que se ejecutó en último lugar.
Esto nos sirve para dos cosas: primero, para saber por qué preguntar cuando chequeemos el valor devuelto por un programa. Y segundo, para devolver los valores establecidos por convenio cuando salgamos de nuestros programas (ya sea hechos con lenguajes de alto nivel o guiones).
La forma de devolver valores al salir del programa es darlos como
parámetro a la orden exit
. Si lo hacemos sin parámetros,
se devolverá un cero.
Por último, un apunte sobre asignar caracteres especiales para el intérprete a variables: cuando nosotros asignamos algo a una variable, se asigna tal cual nosotros lo vemos en la pantalla (cuando son caracteres especiales), y la sustitución se hace cuando la utilicemos en alguna expresión, si entra dentro de una orden. Veamos el siguiente ejemplo como aclaración de lo dicho.
cd /
asterisco=*.txt
cd /home/zoso/cambio
ls $asterisco
Si ejecutamos el siguiente código, la salida de éste será
la lista de ficheros que concuerdan con la expresión regular almacenada
en la variable asterisco
, pero los que se encuentren en el directorio
/home/zoso/cambio
, no los que se encuentren en el directorio
raíz.