Está en la página 1de 23

Tutorial de programación en shell

Página siguiente Página anterior Índice general

Tutorial de programación en shell


Esteban Manchado `ZoSo',
a2092@correo.dis.ulpgc.es
28 de noviembre de 1998

Este tutorial es una introducción al uso del lenguaje del intérprete de órdenes de UNIX como lenguaje
de programación sencillo para automatizar tareas y hacer pequeños programas. No es en absoluto una
guía completa de programación, pero presenta todas las características importantes y da una idea de la
potencia del lenguaje

1. Introducción
2. ¿Qué es un guión (script)?
● 2.1 Comentarios
● 2.2 Redirecciones
● 2.3 El truco del #!

3. Variables
● 3.1 Referenciar variables
● 3.2 ¿Cómo funciona el analizador del intérprete?
● 3.3 Dos órdenes útiles: echo y read
● 3.4 Parámetros
● 3.5 Valores devueltos. Orden exit
● 3.6 Asignación de caracteres especiales

4. Comillas
● 4.1 Tres tipos de comillas
● 4.2 Separadores de parámetros

http://sopa.dis.ulpgc.es/~a2092/posyo/shell/shell.html (1 of 2) [02/07/2001 10:28:00 a.m.]


Tutorial de programación en shell

5. Condiciones
● 5.1 La orden test

6. Control del flujo del programa


● 6.1 Estructura if
● 6.2 Condiciones particulares
● 6.3 Estructura case
● 6.4 Estructura while
● 6.5 Estructura for
● 6.6 Estructura until
● 6.7 Ruptura de la ejecución normal de los bucles

7. Miscelánea
● 7.1 Variables útiles de entorno
● 7.2 Ejecución en el mismo inérprete (shell)
● 7.3 Referirse a variables
● 7.4 La orden eval

8. Colofón
Página siguiente Página anterior Índice general

http://sopa.dis.ulpgc.es/~a2092/posyo/shell/shell.html (2 of 2) [02/07/2001 10:28:00 a.m.]


Tutorial de programación en shell: Introducción

Página siguiente Página anterior Índice general

1. Introducción
Lo primero es lo primero: no soy un experto (ni mucho menos) en la programación en shell, pero hace un
tiempo me interese por ella e intenté aprender lo que pude de un libro y unos apuntes. El libro es ``UNIX
shell programming'', de Stephen G. Kochan y Patrick H. Wood, de Hayden books UNIX system library
(que a su vez pertenece a Prentice-Hall), ISBN 0-672-48448-X. Es necesario que consigas ese libro (que
por otro lado es absolutamente genial) u otro si realmente necesitas saber programar bien en shell. Esto
más que nada es una introducción a la programación, para hacer algunos programas sencillos y para
hacerte una idea de cómo es programar en shell.
La programación en shell se basa en el uso de las herramientas del sistema, y el UNIX es un sistema
operativo (UNIX y sus clones) que cuenta con bastantes herramientas de proceso y filtrado de textos y de
control de procesos, entre otras. Por ello, permite automatizar procesos repetitivos, que hechos a mano
serían engorrosos y lentos.
Para poder utilizar este tutorial hay que tener acceso a un intérprete compatible con el sh, como el propio
sh, el Korn Shell o el bash de Linux. También hay que tener una cierta soltura con sistemas UNIX
(saber lo que son los permisos de los ficheros, conocer las órdenes básicas, etc.), y tener cierta idea de las
cosas que ofrece el intérprete (sustitución de caracteres especiales por nombres de ficheros, redirecciones
de las entradas y salidas, etc.)
Para poder utilizar los conocimientos que pretende facilitar este tutorial, hay que conocer el mayor
número posible de herramientas del sistema, como pueden ser el grep, el wc, sort, tr, sed, cut y
awk (el awk es casi un lenguaje de programación aparte, aunque puede servir perfectamente como
complemento para la programación en shell. Hay un tutorial de awk muy bueno en algún sitio de INet, a
donde tengo un enlace en la página de UNIX).
Si estás acostumbrado o al menos conoces el MS-DOS, puedes pensar que la programación en shell es
algo parecido a los ficheros BAT del primero, aunque la diferencia entre ellos es más o menos igual de
grande que la existente entre los sistemas UNIX y el MS-DOS (por versatilidad, por potencia, por diseño
...).
También sería bastante bueno, en realidad más para manejar las herramientas del sistema que para la
programación en shell en sí, que tuvieras conocimiento de las expresiones regulares (hay una pequeña
lista en mi página de trucos de UNIX).

Y tú te preguntarás: ¿Por qué programar en shell y no en un lenguaje de alto nivel? Pues depende de para
qué lo utilices. Lo mejor es saber las dos cosas y aplicarlas cuando sean más adecuadas. La programación
en shell es muy útil y cómoda para crear programas fácilmente modificables, pequeños, que resuelvan
tareas repetitivas, como por ejemplo comprobar ciertas condiciones cada cierto tiempo (ejecutándolos en
segundo plano, claro), o para ejecutar órdenes muy largas con frecuencia (para no tener que escribir una
orden larga o varias órdenes seguidas todo el tiempo)... Además, si el programa no requiere mucha
potencia pero si comodidad a la hora de manejar ristras y procesar y filtrar texto, es mucho más fácil
programar en shell, que, por ejemplo, en C.

http://sopa.dis.ulpgc.es/~a2092/posyo/shell/shell-1.html (1 of 2) [02/07/2001 10:28:37 a.m.]


Tutorial de programación en shell: Introducción

Página siguiente Página anterior Índice general

http://sopa.dis.ulpgc.es/~a2092/posyo/shell/shell-1.html (2 of 2) [02/07/2001 10:28:37 a.m.]


Tutorial de programación en shell: ¿Qué es un guión (script)?

Página siguiente Página anterior Índice general

2. ¿Qué es un guión (script)?


Un guión o script es un fichero de texto que contiene una serie de instrucciones que se pueden ejecutar
en la línea de órdenes, y que se ejecutarán seguidas. En ese sentido son iguales que los ficheros con
extensión BAT de MS-DOS, aunque, por supuesto, como en los sistemas UNIX no existe el concepto de
extensión, pueden tener cualquier nombre. El único requisito es que ese fichero de texto tenga permiso de
ejecución para la persona que intenta ejecutarlo. Alternativamente, puedes llamar al intérprete y darle
como parámetro el nombre del guión, pero es más cómodo dar permiso de ejecución al guión para
ahorrarnos escribir el nombre del intérprete.
Esas instrucciones se procesarán por orden, como si alguien las escribiera en el mismo orden en la línea
de órdenes (no es exactamente así, pero casi). Por ello, van una por línea (los saltos de línea se
interpretan igual que si hubieras pulsado INTRO), o separadas por caracteres ``punto y coma'' (;) si están
en la misma línea. Cuando se ejecuta un guión para el intérprete de órdenes, se intenta ejecutar como un
binario normal, pero al darse cuenta el sistema operativo de que no es un binario válido, pero que es un
archivo de texto, ejecuta un intérprete hijo y empieza a interpretar el fichero de texto. Si fuera un archivo
con un texto cualquiera, tarde o temprano (probablemente en la primera línea) habría un error de sintaxis
y se abortaría la ejecución del programa.

2.1 Comentarios
Como el resto de los lenguajes de programación (al menos todos los que he visto yo), hay un carácter o
unos caracteres especiales que denotan comentario. Ese texto es ignorado por el intérprete al leer el
guión, y sólo sirven para que una persona, cuando edite el guión, pueda entender cómo funciona. Este
carácter especial es la almohadilla `#', y denota que desde ahí hasta el final de la línea, el resto de los
caracteres son un comentario del programador (como el `//' de C++).

2.2 Redirecciones
Al igual que si fuera un ejecutable cualquiera, un guión permite redireccionamientos de la entrada y de
las salidas. No ocurre lo mismo con los ficheros BAT de MS-DOS, por razones obvias (el MS-DOS está
mal hecho). Las redirecciones de la entrada, salida estándar, errores estándar, y todas las salidas se
realizan con los caracteres <, >, 2> y &> (depende del intérprete, así son las del bash), como estoy
suponiendo que ya sabes. Algo que puede que no sepas es que existe un fichero-dispositivo en UNIX,
estándar a todos los clones (en Linux está), que es el /dev/null. Cualquier cosa que es escriba en ese
dispositivo virtual va a parar a un agujero negro, es decir, se ignora completamente. ¿Y para qué sirve
esto?, puede que te preguntes. Pues si quieres comprobar si un programa devuelve un error, pero no
quieres que la salida estándar aparezca en pantalla (porque al usuario no le interesa, por ejemplo),
entonces redireccionas la salida total (con &>) a /dev/null.

http://sopa.dis.ulpgc.es/~a2092/posyo/shell/shell-2.html (1 of 2) [02/07/2001 10:29:06 a.m.]


Tutorial de programación en shell: ¿Qué es un guión (script)?

2.3 El truco del #!


Por lo general, en los guiones de los sistemas UNIX se suele incluir una línea especial, que empieza por
#! (deben ser los dos primeros caracteres del fichero). La historia de esto es larga, y no la sé
exactamente, pero el caso es que sirve para ejecutar un fichero con un intérprete determinado (no sólo
intérpretes de órdenes como bash o el intérprete C, sino también como Perl, Tcl o Python).
Lo único que debes saber es que todos tus guiones deben empezar con una línea como:

#!/bin/bash
En general, debe empezar con los caracteres #!, seguidos de la ruta completa al intérprete que desees
utilizar para ese guión. Si no utilizas características específicas de bash, deberías poner #!/bin/sh.
¿Y cómo sabes qué intérprete quieres utilizar? Por lo general, bastará con /bin/sh. Si utilizas
características específicas del intérprete Korn o del bash, entonces deberías utilizar /bin/ksh o
/bin/bash respectivamente. Si el intérprete fuera del lenguaje Perl, por ejemplo (nada que ver con
este tutorial), querrías que se ejecutara el programa perl y que recibiera como parámetro el fichero en el
que has escrito esa línea mágica. Entonces, para hallar la ruta completa al programa perl, escribes
which perl, y el sistema te responderá algo como /usr/bin/perl o /usr/local/bin/perl.
Tú pones la línea que te devuelva y listo.

Página siguiente Página anterior Índice general

http://sopa.dis.ulpgc.es/~a2092/posyo/shell/shell-2.html (2 of 2) [02/07/2001 10:29:06 a.m.]


Tutorial de programación en shell: Variables

Página siguiente Página anterior Índice general

3. Variables
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.

3.1 Referenciar variables


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!).

http://sopa.dis.ulpgc.es/~a2092/posyo/shell/shell-3.html (1 of 4) [02/07/2001 10:29:22 a.m.]


Tutorial de programación en shell: Variables

3.2 ¿Cómo funciona el analizador del intérprete?


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.

3.3 Dos órdenes útiles: 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

http://sopa.dis.ulpgc.es/~a2092/posyo/shell/shell-3.html (2 of 4) [02/07/2001 10:29:22 a.m.]


Tutorial de programación en shell: Variables

demasiadas palabras

3.4 Parámetros
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).

3.5 Valores devueltos. Orden 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.

http://sopa.dis.ulpgc.es/~a2092/posyo/shell/shell-3.html (3 of 4) [02/07/2001 10:29:22 a.m.]


Tutorial de programación en shell: Variables

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.

3.6 Asignación de caracteres especiales


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.

Página siguiente Página anterior Índice general

http://sopa.dis.ulpgc.es/~a2092/posyo/shell/shell-3.html (4 of 4) [02/07/2001 10:29:22 a.m.]


Tutorial de programación en shell: Comillas

Página siguiente Página anterior Índice general

4. Comillas
El uso de las comillas en la programación en shell es un tema bastante importante. En los lenguajes de
alto nivel normalmente las comillas sólo se utilizan para las ristras, y no necesitan un aprendizaje
especial para usarlas: simplemente saber (en el lenguaje C, por ejemplo) que las comillas simples son
caracteres y que las comillas dobles requieren reservar memoria, porque son ristras, o sea, punteros a
caracteres.

4.1 Tres tipos de comillas


En la programación en shell en UNIX hay tres tipos de comillas útiles: La comilla simple ('), la comilla
doble (") y la comilla invertida (`). Cada una tiene un propósito diferente, y se deben saber usar todas
bien.
La comilla simple (') sirve para coger literales. Es importante darse cuenta de que algo encerrado entre
comillas simples siempre es un solo parámetro, aunque sea una ristra vacía ('').
La comilla doble sirve para sustituir lo que hay dentro (valores de variables, caracteres especiales), pero
después de sustituir queremos tratarlo como si estuviera encerrado entre comillas simples. Esto es útil
cuando, por ejemplo, tenemos que dar un parámetro a un programa, pero ese parámetro es leído del
teclado y no sabemos si la ristra es vacía. Si nos tenemos que asegurar de que se trata como un
parámetro, tendríamos que encerrarlo entre comillas simples (recordemos que '' sigue siendo un
parámetro, aunque vacío), pero si lo encerramos entre comillas simples no se sustituirá la variable. La
forma de hacerlo es encerrando la variable entre comillas dobles.
Veamos esto en el siguiente ejemplo:

x=100
y='500$x'
echo $y # Esto enseñará `500$x' en pantalla
y="500$x"
echo $y # Esto enseñará `500100' en pantalla
Las que quedan son las comillas invertidas (`). Estas se utilizan con órdenes, y se sustituyen por la salida
del programa que está entre las comillas invertidas. Por ejemplo, si queremos almacenar en una variable
el número de usuarios que hay actualmente en el sistema, podemos hacer

usuarios=`who | wc -l`
Por si queda alguna duda, who dice los usuario que hay en el sistema, poniendo uno en cada línea, y wc
-l cuenta las líneas que tiene el texto que le entre por la entrada estándar, sacando el número resultante a
la salida.

http://sopa.dis.ulpgc.es/~a2092/posyo/shell/shell-4.html (1 of 2) [02/07/2001 10:29:36 a.m.]


Tutorial de programación en shell: Comillas

4.2 Separadores de parámetros


Hay otras razones para desear poner parámetros encerrados entre comillas simples o dobles: si queremos
el literal exacto del contenido de una variable.
Si una variable (por ejemplo, porque se le ha asignado la salida estándar de un programa) contiene saltos
de línea, y no ponemos comillas, entonces el intérprete lo entenderá como que esos saltos de línea son
separadores de parámetros, y por ejemplo el echo los separará con un espacio. Tendremos un problema
parecido si ponemos más de un espacio para separar los parámetros:

x='10 y 5 espacios'
echo $x # Saldrá en pantalla '10 y 5 espacios'
echo `$x' # Saldrá en pantalla `$x'
echo "$x" # Saldrá en pantalla '10 y 5 espacios'
¿Queda claro? Pues eso.

Página siguiente Página anterior Índice general

http://sopa.dis.ulpgc.es/~a2092/posyo/shell/shell-4.html (2 of 2) [02/07/2001 10:29:36 a.m.]


Tutorial de programación en shell: Condiciones

Página siguiente Página anterior Índice general

5. Condiciones
Manejar condiciones es importante en todos los lenguajes de programación: sirven para controlar el flujo del
programa (el título del próximo capítulo), y eso lo es prácticamente todo.
En UNIX, al contrario de lo que podría parecer, el valor entero que representa al valor verdadero booleano es el 0,
y el valor entero que representa al falso es el 1. Esto es así por el convenio antes citado de los valores devueltos por
los programas en UNIX.

5.1 La orden test


Para ello, en UNIX existe la orden test. Tiene una sintaxis alternativa, que es con mucho la más usada (es más
clara), que es con corchetes ([]). Es importante darse cuenta de que la segunda sintaxis es también un programa
(que se llama [, aunque suene raro, y que exige, supongo, que al final de la condición se ponga un ], más por
estética que por otra cosa), así que necesita que lo que pongamos entre los corchetes esté separado por al menos un
espacio (incluido el último corchete).
La orden test (con cualquiera de las dos sintaxis) acepta dos tipos de operadores: los unarios (que sólo tienen un
parámetro) y los binarios (que necesitan dos parámetros). Si al principio de una condición ponemos un `!',
separado por espacios de lo demás, como siempre, negaremos la condición que estemos poniendo en el test.

Operadores para el manejo de ficheros


Todos los operadores para el manejo de ficheros en la programación en shell son unarios. Cuando se utilizan los
operadores unarios con la orden test, la sintaxis es la siguiente: test operador fichero. Los operadores
de ficheros que acepta la orden test son los siguientes:

Operador Significado
-f Si fichero es un fichero ordinario
-d Si fichero es un directorio
-r Si el proceso tiene permiso de lectura sobre fichero
-w Si el proceso tiene permiso de escritura sobre fichero
-x Si el proceso tiene permiso de ejecución sobre fichero
-s Si fichero tiene una longitud mayor que cero

Operadores para el manejo de ristras


Los operadores para el manejo de ristras en la programación en shell son dos binarios, y la dos unarios (en realidad
dan las condiciones contrarias). La sintaxis de test con operadores binarios, como era de esperar, es test
operando1 operador operando2. La tabla de operadores de manejo de ristras es la siguiente:

Operador Significado
= (binario) Si operando1 es igual a operando2
!= (binario) Si operando1 es distinto de operando2
-z (unario) Si operando es una ristra nula
-n (unario) Si operando es una ristra no nula

http://sopa.dis.ulpgc.es/~a2092/posyo/shell/shell-5.html (1 of 2) [02/07/2001 10:30:04 a.m.]


Tutorial de programación en shell: Condiciones

Operadores para el manejo de números


Vale, vale. Ya sé que he dicho que no existen los tipos en las variables del intérprete, pero aún así es posible que la
ristra ``1'' o ``20'' sea tratada como un número, así que todavía son necesarios los operadores para el manejo de
números. Los operadores de manejo de números son siempre binarios, y son los siguientes:

Operador Significado
-eq operando1 y operando2 son iguales
-ne operando1 y operando2 son distintos
-gt operando1 es mayor estricto que operando2
-ge operando1 es mayor o igual que operando2
-lt operando1 es menor que operando2
-le operando1 es menor o igual que operando2

Expresiones complicadas
Algunas veces vas a necesitar evaluar expresiones complicadas en un guión, como condiciones dobles (con y o con
o), y puede que incluso condiciones triples (más lo dudo).
Este es un tema delicado en la programación de shell: la forma de hacerlo es con los operadores -a y -o (and y or
respectivamente), con lo que una expresión doble puede quedar tal que así (de horrible):

[ -w /home/zoso/.profile -a -n $MAIL ]
¿Feo, verdad? Esto comprueba si el proceso tiene permiso de escritura sobre el fichero /home/zoso/.profile
y si la variable $MAIL tiene algún contenido.
Pues todavía no ha llegado lo peor: Si quisiésemos realizar condiciones dobles, y poner paréntesis, hay un
problema añadido, y es que los paréntesis son caracteres especiales para el intérprete. Entonces, para que no los
reconozca como paréntesis, y que coja los paréntesis literales, como texto, hay que escribirlos como \( y \) (sí,
utilizando el símbolo \ como en C y en otros lenguajes de programación relacionados con el UNIX). Así, una
condición doble quedaría en el lenguaje del intérprete así que ilegible:

[ -d ~/Mail -o \( -f ~/mail -a $cont -ne 0 \) ]


Este ejemplo comprueba si existe el directorio ~/Mail o si existe el fichero -f ~/mail y además la variable
$cont, interpretada como un número, es distinta de 0. Es importante acordarse de escribir espacios entre los
paréntesis, todos los operadores y los corchetes, porque al fin y al cabo van a ser interpretados por un programa que
espera que se presenten todos como parámetros diferentes.

Página siguiente Página anterior Índice general

http://sopa.dis.ulpgc.es/~a2092/posyo/shell/shell-5.html (2 of 2) [02/07/2001 10:30:04 a.m.]


Tutorial de programación en shell: Control del flujo del programa

Página siguiente Página anterior Índice general

6. Control del flujo del programa


Ahora es cuando vamos a empezar a ser capaces de programar algo en shell. Las estructuras de control de flujo del
programa que tenemos disponibles cuando programamos en shell son el if, case, while, for y until. Además,
veremos algunas órdenes especiales y algunas construcciones un poco más raras, que sirven también para controlar el
curso de los acontecimientos.

6.1 Estructura if
La estructura if tiene una sintaxis algo inusual, porque necesita la palabra then, pero en la línea siguiente a donde
está el if y la condición. Es un if bastante versátil, ya que permite cláusulas elif (else if) y else. La palabra para
indicar el fin de la estructura if es la palabra fi (if al revés).
La cláusula elif, por si no lo sabes, es parecida al else, aunque necesita una condición después. Para comprender
mejor el funcionamiento, veamos la siguiente equivalencia:

if [ condicion ] if [ condicion ]
then then
... ...
else elif [ condicion2 ]
if [ condicion2 ] then
then ...
... fi
fi
fi
Algunas personas, para hacer la sintaxis más clara, o al menos más parecida a otros lenguajes, como Pascal, escriben
el if de la siguiente forma:

if [ condicion ]; then
fi
Para decidir qué ejecutar, la estructura if permite, además de una condición, el nombre de un programa. Lo que se
hará entonces es ejecutar el programa con los parámetros dados, y dar como verdadero (y ejecutar lo que haya entre el
if y el fi o el elif o el else) que el programa devuelva un cero.
En estos casos es muy útil la instrucción nula (:), ya que si lo que queremos es ejecutar un código si el programa va
mal, entonces la única forma de hacerlo es la siguiente:

if orden
then
:
# No hacemos nada
else
codigo
...
fi

http://sopa.dis.ulpgc.es/~a2092/posyo/shell/shell-6.html (1 of 5) [02/07/2001 10:30:55 a.m.]


Tutorial de programación en shell: Control del flujo del programa

Como ejemplo tomaremos unas líneas que están en mi fichero ~/.bashrc:

if set | grep DISPLAY &>/dev/null; then


alias vi="vi -g"
fi
Esto ejecuta la orden set, que da las variables de entorno. Las filtra para quedarnos sólo con la que contenga la
variable $DISPLAY (si es que está), y manda toda la salida a un agujero negro (/dev/null). Esto se hace para
comprobar el código de salida del grep, que devolverá verdadero si deja ``salir'' alguna línea. Es decir: si está
definida la variable $DISPLAY, se entra a ejecutar el alias (en realidad, se entra si existe la variable $DISPLAY o
si alguna variable tiene como contenido algo con la ristra DISPLAY, lo cual es bastante improbable. Hay una forma
de afinar la condición, te lo dejo como ejercicio). Si el grep devuelve falso, no se ejecuta nada.

6.2 Condiciones particulares


Hay dos condiciones que son relativamente comunes, que son ejecutar un código si algo inmediatamente anterior ha
ido bien, o justo lo contrario, ejecutarlo sólo si lo anterior ha ido mal.
Por ello, hay dos ``estructuras'' que nos permiten manejar estas situaciones de una forma más cómoda y limpia. Estas
estructuras son && y ||. Para las personas que hayan programado en C, les resultarán familiares. El && es el
equivalente a la palabra reservada de Pascal and y || es el equivalente del or en Pascal.
La forma de entender qué produce cada estructura, es pensar que el intérprete sólo va a ejecutar lo estrictamente
necesario para saber cómo termina la condición. Sabemos que false && cualquiercosa da falso, y que true
|| cualquiercosa da verdadero, así que eso es lo que va a hacer el intérprete: si utilizamos la estructura &&,
ejecutará la primera instrucción, y sólo si va bien (si devuelve un cero) ejecutará la segunda, y análogamente con la
estructura ||, el intérprete ejecutará la primera instrucción, y sólo ejecutará la segunda si la primera ha ido mal, es
decir, si el valor de la condición total depende de lo que pase al ejecutar la segunda instrucción.
La forma de acordarse es pensarlo como en lenguaje natural: ``Hazlo y te odiaré para siempre''. O sea, que si la
primera instrucción se ejecuta (...satisfactoriamente; esto es, devolviendo un 0), pasará la segunda. Análogamente,
``Hazlo o te odiaré para siempre''. O sea, si la primera no se ejecuta (satisfactoriamente), se ejecutará la segunda. Pero
dejémosnos de amenazas ;-).
La estructura && puede utilizarse cuando ejecutemos una instrucción cuyo trabajo depende de que la primera haya ido
bien, y la || puede ejecutarse para dar mensajes de error:

cd foo || echo "¡No puedo entrar en 'foo'!"

6.3 Estructura case


La estructura case del Bourne Shell y compatibles es una estructura parecida a la de Pascal: cuando entra en la
estructura, sale cuando empiece la siguiente etiqueta (no así en C, en el que las etiquetas del case actúan como
etiquetas de un JMP en ensamblador: una vez que entran en una etiqueta, no salen del case hasta que éste acaba
completamente).
Las etiquetas pueden ser cualquier ``expresión regular'' (expresión con comodines típica de los intérpretes de órdenes;
no las expresiones regulares del vi, el grep, el perl y otros) válida, con lo que se puede poner un * como última
etiqueta del case para actuar como else. Para indicar que termina el código de una etiqueta determinada, hay que
poner al final de la última orden dos signos de punto y coma seguidos.
La estructura general del case es:

http://sopa.dis.ulpgc.es/~a2092/posyo/shell/shell-6.html (2 of 5) [02/07/2001 10:30:55 a.m.]


Tutorial de programación en shell: Control del flujo del programa

case valor
in
expreg1)
...
ultimaorden1;;
expreg2)
...
ultimaorden2;;
...
expregn)
...
ultimaordenn;;
esac
Por ejemplo, si queremos comprobar una respuesta de sí o no (de forma un poco relajada), podemos hacer:

case $resp; in
s*)
echo "Has contestado sí (o algo parecido)"
n*)
echo "Has contestado no (o algo parecido)"
*)
echo "Has contestado alguna otra cosa"
esac

6.4 Estructura while


La estructura básica para construir bucles. La sintaxis es:

while orden
do
...
done
En orden podemos poner una orden normal y corriente (el bucle se ejecutaría mientras la orden devolviera un cero), o
podríamos también poner una condición, con la orden test (o mejor, con la sintaxis alternativa []), que por otra
parte no deja de ser una orden como otra cualquiera.
Si quisiésemos estar seguros de que la contestación a la pregunta del ejemplo anterior era s o n, podríamos haber
hecho lo siguiente:

while [ $resp != "s" -a $resp != "n" ]; do


read resp
done
Y justo debajo podríamos poner el case anterior, para comprobar cuál fue finalmente la respuesta.

http://sopa.dis.ulpgc.es/~a2092/posyo/shell/shell-6.html (3 of 5) [02/07/2001 10:30:55 a.m.]


Tutorial de programación en shell: Control del flujo del programa

6.5 Estructura for


Es otra de las estructuras importantes en los lenguajes de programación. Es más versátil que el equivalente de Pascal,
pero menos que el equivalente de C (como era lógico, por otra parte).
Es una estructura que permite dos sintaxis: La primera, la del for más o menos tradicional (en los lenguajes
interpretados), es decir, dar el nombre de una variable y los valores por los que tiene que ``pasar'', es la siguiente:

for variable in expreg1 expreg2 ... expregn


do
...
done
Hay una pequeña diferencia respecto a los bucles normales for, y ésta es que los valores por los que pasa la variable
variable tenemos que especificarlos uno a uno (mediante expresiones regulares). Las expresiones regulares se
intentarán hacer coincidir con los nombres de los ficheros del directorio actual.
Por ejemplo, si quisiéramos coger todos los ficheros normales y hacerles una sustitución con el sed, por ejemplo, una
opción sería utilizar un bucle for. Por ejemplo, yo utilizo de vez en cuando bucles así para pasar texto de MS-DOS a
texto UNIX:

for fichero in *.txt; do


sed 's/^M//' $fichero >TEMPORAL
mv TEMPORAL $fichero
done
En este bucle, cogemos el sed y sustituimos los caracteres de retorno de carro por nada, o sea, los borramos. Esto
convierte los ficheros de texto de MS-DOS a UNIX. Pero sed manda el resultado a la salida estándar, así que
tenemos que dirigirlo a otro fichero (el TEMPORAL. Aquí suponemos que no existe ningún fichero llamado
TEMPORAL en el directorio actual, o por lo menos que no es importante y lo podemos borrar). Una vez tenemos el
resultado en el fichero TEMPORAL, simplemente lo movemos al fichero original, sobreescribiendo el fichero antiguo y
poniendo la versión nueva, sin los caracteres de retorno de carro.
La segunda sintaxis que permite el bucle for es una sintaxis sin lista. Si utilizamos esta sintaxis, el intérprete lo
entenderá como que la lista que queremos es la de todos los parámetros dados al guión desde la línea de órdenes (u
otro guión o programa). La sintaxis queda:

for variable
do
...
done
Si tuviéramos un programa que sólo aceptara ficheros u opciones, podríamos discriminar entre unos y otras así:

for par; do
case $par; in
-*)
echo "Opción '$par' (empieza por '-')"
*)
echo "Fichero '$par' (cualquier otra cosa)"
esac
done

http://sopa.dis.ulpgc.es/~a2092/posyo/shell/shell-6.html (4 of 5) [02/07/2001 10:30:55 a.m.]


Tutorial de programación en shell: Control del flujo del programa

6.6 Estructura until


Esta estructura es parecida a la while, aunque la condición la damos ``al revés'', es decir, que el bucle se ejecuta
mientras la condición a comprobar sea falsa, y termina cuando sea verdadera. Tiene una utilidad diferente que en los
lenguajes de programación. Normalmente se utiliza en los lenguajes de alto nivel para forzar la ejecución del bucle al
menos una vez, aunque la condición sea desde el principio verdadera. No se puede utilizar de esa manera en la
programación en shell, porque la sintaxis tiene la condición al principio.
Su uso es sólo para mejorar la legibilidad, porque podemos negar incluso la salida de los programas que llamemos,
anteponiendo a la llamada al programa el caracter de negación !.
La sintaxis es como sigue:

until orden
do
...
done

6.7 Ruptura de la ejecución normal de los bucles


Algunas veces necesitamos romper la ejecución normal de los bucles. Quizás queramos saltarnos lo que queda de
iteración, o quizás necesitemos en un momento dado salir del bucle.
Las dos órdenes utilizadas en la programación en shell para hacer estas cosas son las dos mismas que se utilizan en
lenguaje C, y se utilizan de la misma manera: break y continue.
La primera sirve para salir del bucle y seguir la ejecución del programa, y la segunda sirve para saltarnos lo que queda
de iteración y empezar la siguiente. Antes de empezar con la siguiente iteración, se volverá a chequear la condición
(supongo).

Página siguiente Página anterior Índice general

http://sopa.dis.ulpgc.es/~a2092/posyo/shell/shell-6.html (5 of 5) [02/07/2001 10:30:55 a.m.]


Tutorial de programación en shell: Miscelánea

Página siguiente Página anterior Índice general

7. Miscelánea
Todo lo que se necesita para empezar a investigar y para despertar tu interés (si a estas alturas la programación
en shell no ha despertado tu interés, dudo que lo haga) ya se ha visto. Lo único que queda son decir algunas
cosas más, y decir que si crees que le falta algo de potencia, que te mires algún libro. Hay algunas cosas que yo
no he querido poner en este documento, como por ejemplo atrapar las señales del sistema, una orden para
manejar los parámetros estilo UNIX (es decir, con - al principio, y pudiendo agrupar -l -F en -lF), y otras
cosas.

7.1 Variables útiles de entorno


En los programas muchas veces es útil consultar las variables de entorno. Por ejemplo, el usuario puede tener
definido su editor preferido enla variable $EDITOR. Así, si necesitamos editar un texto, podemos llamar al
editor que ponga la variable $EDITOR, en vez de coger el que a nosotros nos dé la gana (ya se sabe, la guerra
santa vi-emacs, en la que por cierto soy defensor del vi, hace estragos). Algunas variables útiles de entorno
son:

Variable Significado
$PS1 Aspecto del apuntador (prompt) principal
$PS2 Aspecto del apuntador (prompt) secundario
$PWD Directorio actual
$HOME Directorio home del usuario que ejecuta el proceso
$USER Nombre de login del usuario que ejecuta el proceso
$$ PID del proceso
$PATH Ruta de búsqueda del usuario actual
El apuntador, como probablemente sabes, es lo que sale a la izquierda de la pantalla cuando escribes cosas. En
una terminal UNIX, probablemente sea `$' en Linux seguramente es algo más complicado.
A lo mejor lo que no sabes es qué diablos es el apuntador secundario. Cuando escribes comillas (de cualquier
tipo) en la línea de órdenes, pero pulsas Enter antes de terminar de escribir lo que tenías entre comillas (es decir,
si abres comillas pero no las cierras), el intérprete enseñará el apuntador secundario en la siguiente línea, y
seguirá poniendo apuntadores secundarios en las líneas subsiguientes hasta que cierres las comillas.
El PID de un proceso es su process id. Es un número que sirve para identificar al proceso (cuando haces un kill
y similares). Este número es evidentemente único para cada proceso, y lo podemos utilizar para crear ficheros en
el directorio /tmp únicos para esa interpretación del guión. Piensa que si tuviéramos a dos usuarios diferentes
ejecutando el mismo guión a la vez, uno de ellos machacaría algún hipotético fichero temporal que pudiera tener
el primero, y se formaría un pollo que no veas.
Algo a tener en cuenta cuando estemos programando apoyándonos en variables de entorno, es que estas
variables no se pueden modificar. Podemos asignarles valores y cambiarán, pero sólo en nuestro programa. Hay
que tener en cuenta que $PWD, que es la variable que controla el directorio actual, no tiene nada que ver con el
núcleo del sistema operativo, es un problema exclusivo del intérprete, que es al fin y al cabo la interfaz para que
el usuario se comunique con el sistema operativo. Así, si escribimos un guión que cambie el directorio actual,
aunque sea mediante la orden cd, al salir del guión del intérprete ni se habrá enterado.

http://sopa.dis.ulpgc.es/~a2092/posyo/shell/shell-7.html (1 of 3) [02/07/2001 10:32:20 a.m.]


Tutorial de programación en shell: Miscelánea

7.2 Ejecución en el mismo inérprete (shell)


El hecho de que nosotros modifiquemos una copia de las variables es por el hecho de que los guiones se
ejecutan en otro intérprete diferente al actual, y entonces las variables de entorno simplemente se copian. Para
que se copien, debemos exportar las variables. Todas las variables normales ya están exportadas en el fichero de
configuración (ya sea en el general o en el de cada usuario), pero quizás algunas variables inventadas por
nosotros o poco comunes queremos que sean exportadas también, para que cualquier otro programa pueda
acceder a ellas para consultarlas.
La manera de forzar esto es con la orden export. La sintaxis es, simplemente, export variable. Por
ejemplo, export EDITOR.
Pero nosotros hablábamos de ejecutar programas en el intérprete actual, entre otras cosas para poder modificar
variables de entorno. La forma de hacer esto, es poner un punto (.) antes del nombre del programa. Por ejemplo,
escribir . inicializacion.

7.3 Referirse a variables


Cuando vimos la forma de referirse a los parámetros, salió el delicado tema de que sólo podíamos referirnos a
los parámetros que estén en las primeras nueve posiciones. Esto es así en el Bourne Shell, y se debe actuar así si
se quiere hacer un guión estándar que se ejecute en cualquier sitio. Pero en el Korn Shell (y creo que también en
bash, aunque no lo he probado) se pueden escribir los nombres de variables con esta sintaxis: ${variable}.
El Bourne Shell acepta esta construcción cuando son variables normales, pero no cuando lo utilizamos para
indicar parámetros.
Así, el Korn Shell y compatibles, podríamos referirnos al parámetro 15 con la expresión ${15}. En cualquier
inérprete compatible con el bourne, si quisiéramos escribir caracteres justo después del contenido de una
variable, deberíamos escribir, por ejemplo:

${foo}bar
Porque si pusiéramos simplemente

$foobar
El intérprete creería que nos referimos a una variable no declarada llamada foobar, con lo que esa expresión
sería una ristra vacía.

7.4 La orden eval


Hay una orden que algunas veces nos puede sacar de algunos apuros: es la orden eval. En realidad esta orden
no sirve para nada, simplemente, al ser una orden (como parámetros tenemos que poner otra orden), fuerza que
el intérprete pase dos veces por el mismo sitio, y que evalúe dos veces la expresión. Así, si quisiéramos que el
usuario introdujera por teclado el nombre de una variable a modificar, podríamos resolver el problema así:

read nombre
eval $nombre=10

http://sopa.dis.ulpgc.es/~a2092/posyo/shell/shell-7.html (2 of 3) [02/07/2001 10:32:20 a.m.]


Tutorial de programación en shell: Miscelánea

La primera vez que el intérprete lo mirara, la expresión de eval quedaría como `[contenido de la variable
$variable]=10'. La segunda vez, ejecutaría la orden, como quedara, con lo que conseguiríamos asignar 10 a una
variable cuyo nombre ha sido elegido por el usuario.

Página siguiente Página anterior Índice general

http://sopa.dis.ulpgc.es/~a2092/posyo/shell/shell-7.html (3 of 3) [02/07/2001 10:32:20 a.m.]


Tutorial de programación en shell: Colofón

Página siguiente Página anterior Índice general

8. Colofón
Bueno, espero que haya servido para algo todo esto (he perdido demasiado tiempo escribiendo y
escribiendo para que al final no sirva de nada), y todo ese rollo, escríbeme a a2092@correo.dis.ulpgc.es
si quieres preguntar algo, etc, etc. y sobre todo, si hay algún error de cualquier tipo (gramatical,
sintáctico, me expreso como el culo, algún error que hace que cuando ejecutes tu primer guión guiándote
por este documento tu ordenador explote...).

Página siguiente Página anterior Índice general

http://sopa.dis.ulpgc.es/~a2092/posyo/shell/shell-8.html [02/07/2001 10:33:00 a.m.]

También podría gustarte