[NOTE] Programaciín en Bash [Practica]

Publicado por D3M0N, 18 de Marzo de 2011, 06:48:56 PM

Tema anterior - Siguiente tema

0 Miembros y 1 Visitante están viendo este tema.

D3M0N

Un shell es un programa a medio camino entre el usuario y el sistema operativo. En UNIX hay multitud de shells, pero quizí¡ uno de los mí¡s frecuentes es el Bourne Shell y su mejora Bourne Again Shell (bash). Cada uno de los shells que existen tienen particularidades que lo hacen ínico, pero tambiín muchas similitudes que nos permiten, una vez aprendido uno, trabajar con los demí¡s sin demasiados problemas. En nuestro caso, trabajaremos con el Bourne Again Shell (bash), ya que es el shell que trae Guadalinex por defecto.

El desarrollo de esta unidad didí¡ctica o lecciín estí¡ pensado para personas que han tenido contacto con algín tipo de lenguaje de programaciín y que conoce los conceptos de variable, rutina, sentencia de control, í¡mbitos locales de declaraciín de identificadores, etc.

1.Primer contacto

En el momento en el que una persona obtiene una cuenta en una mí¡quina UNIX, el administrador le asigna una shell de trabajo que serí¡ el que le darí¡ la bienvenida cada vez que inicie una sesiín en esa mí¡quina.

Para averiguar cuí¡l es el shell de trabajo que le ha asignado su administrador tan sílo tiene que ejecutar en el terminal el siguiente comando que muestra en pantalla el contenido de la variable $SHELL. Esta es la variable es la que el sistema utiliza de forma estí¡ndar para guardar el nombre del shell de trabajo.

Código: php
$ echo $SHELL


Si no aparece la cadena /bin/bash que identifica al deberí¡ cambiar su shell de trabajo para poder continuar esta lecciín. Para ello tan sílo debe ejecutar el comando chsh.

Al hacerlo aparecerí¡ una lista con los nombres completos de todos los shell disponibles en su sistema e instrucciones paso a paso que le permitirí¡n cambiarlo de una forma muy sencilla.

A lo largo de toda la lecciín iremos mostrando numerosos ejemplos que nos permitirí¡n ilustrar cada uno de los conceptos estudiados. En algunos de ellos al final de algunas lí­neas aparece un texto en castellano precedido del sí­mbolo #. Se trata de comentarios que bash shell ignora por completo.

2.Desví­o de la salida y la entrada de los comandos

El bash shell permite desviar la entrada y la salida de todos los comandos de forma que estos puedan leer o escribir sus datos en un fichero, en la pantalla, en una lí­nea de comunicaciones o en cualquier otro dispositivo sin que sea preciso cambiar una sola lí­nea del cídigo fuente.

Para desviar, por ejemplo, la salida del comando ls a un fichero llamado lst basta con teclear el siguiente comando:

Código: php
$ ls -F > lst


Este comando crea primero el fichero lst y a continuaciín ejecuta ls, pero desviando toda la salida que produzca hacia este fichero. Tambiín es posible desviar la salida de un comando aíñadiíndola a un fichero ya existente. Por ejemplo, si quisiíramos aíñadir al fichero anterior la frase "estos eran mis ficheros", bastarí­a con teclear el siguiente comando:

Código: php
$ echo "estos eran mis ficheros" >> lst


Si ahora mostramos en pantalla el contenido de lst obtendrí­amos el siguiente resultado:

Código: php
$ cat lst
a.c b.c buf.c copia/ f1.dat f2.dat f3.dat
estos eran mis ficheros


La entrada estí¡ndar tambiín se puede desviar. Por ejemplo, si deseamos enviar al usuario juan el fichero anterior, podemos indicar al comando mail que lea el texto a enviar desde este fichero de la siguiente forma:

Código: php
$ mail juan < lst


Tambiín es posible desviar la entrada usando el sí­mbolo <<, pero en este caso el resultado es diferente. Cuando tecleamos un comando de la forma

Código: php
$ cmd << palabra


El shell crea un fichero temporal en el que introduce todas las lí­neas que lea de la entrada estí¡ndar hasta encontrar una que contenga tan sílo la palabra indicada. A continuaciín ejecuta el comando desviando su entrada a travís ese fichero temporal. En cierto modo, algo como:

Código: php
$ mail juan << fin
Hola Juan, te he enviado el listado de mis ficheros
fin


es equivalente a la secuencia de comandos

Código: php
$ echo "Hola Juan, te he enviado el listado de mis ficheros" > tmp
$ mail juan < tmp
$ rm tmp


Este tipo de desví­o se utiliza de forma casi exclusiva en los programas que construyamos en el lenguaje del shell. En sesiones interactivas prí¡cticamente no se utiliza.

3.Nombres de ficheros y metacaracteres

Todos los shell de UNIX permiten el uso de metacaracteres para representar de forma simplificada conjuntos mí¡s o menos amplios de nombres de ficheros cuyos nombres encajan en un cierto patrín. A continuaciín se muestran los cuatro metacaracteres existentes:

● *, que concuerda con cualquier cadena de caracteres, incluida la cadena vací­a.
● ?, que concuerda tan sílo con un carí¡cter.
● [a1a2 ... an], que concuerda con cualquiera de los caracteres entre los corchetes. Es posible especificar un rango separando el carí¡cter inicial y final del mismo mediante un guiín. Por ejemplo [az] encaja en cualquier letra miníscula (exceptuando la íñ).
● [!a1a2 ... an], que concuerda con cualquier carí¡cter que no aparezca entre los corchetes. Como en el caso anterior, es posible especificar rangos de caracteres usando el guiín.

A continuaciín se muestran varios ejemplos de uso de los metacaracteres:

Código: php
$ ls FR
a.c b.c buf.c copia/ f1.dat f2.dat f3.dat
./copia:
a.c b.c buf.c f1.dat f2.dat f3.dat
$ # ficheros cuyo nombre terminan en .c
$ls *.c
a.c b.c buf.c
$ # ficheros .c cuyo nombre base tiene un carí¡cter
$ ls ?.c
a.c b.c
$ # ficheros cuyo nombre incluye al menos un dí­gito
$ ls *[09]*
f1.dat f2.dat f3.dat
$ # ficheros .c con nombre base de un carí¡cter bajo el directorio actual
$ ls */?.c
copia/a.c copia/b.c
$ # ficheros cuyo nombre tiene tres caracteres y el central es un punto
$ # en el directorio actual y en los inmediatamente inferiores
$ ls ?.? */?.?
a.c b.c copia/a.c copia/b.c


En el ejemplo hemos mostrado diversos usos de los patrones con el comando ls, pero se pueden utilizar casi en cualquier lugar y siempre que el shell los encuentra los sustituye de forma automí¡tica por la lista completa de nombres de ficheros que encajan en el mismo. Por ejemplo:

Código: php
$ echo "Estos son los ficheros de mi directorio: " *
Estos son los ficheros de mi directorio: a.c b.c c.c


4.Comillas y caracteres de escape

Tal y como hemos podido ver hasta ahora, el shell reserva para uso propio ciertos caracteres. Algunos de ellos son poco comunes en la escritura, pero otros son de uso frecuente y se necesita del algín mecanismo que nos permita impedir al shell los interprete con su significado especial.

El primer mecanismo consiste en escribir esos caracteres entre comillas simples o dobles. En el primer caso, todo lo que escribamos entre comillas se interpretarí¡ literalmente, esto es, si aparece un * se interpretarí¡ como el carí¡cter asterisco y no como la lista de todos los nombres ficheros en el directorio actual. En el segundo caso tan sílo se interpretarí¡n literalmente los metacaracteres, mientras que las variables serí¡n sustituidas por sus valores. De momento tan sílo conocemos la variable $SHELL que nos permite conocer cuí¡l es el nombre de nuestro shell de trabajo, pero serí¡ suficiente para ilustrar el comportamiento de las comillas.

Código: php
$ ls
f.dat main.c
$ echo ’$SHELL *’
$SHELL *
$ echo "$SHELL *"
/bin/bash *
$ echo $SHELL *
/bin/bash a.c b.c buf.c copia f1.dat f2.dat f3.dat


Las comillas permiten delimitar una cadena de texto. Si tan sílo queremos interpretar literalmente un carí¡cter podemos precederlo de una barra invertida.

Código: php
$ ls
f.dat main.c
$ echo \$SHELL \*
$SHELL *
$ echo "\$SHELL \*"
$SHELL *
$ echo $SHELL *
/bin/bash a.c b.c buf.c copia f1.dat f2.dat f3.dat


5.Tuberí­as y comunicaciones: pipes

Situaciones en las que la salida de un comando se utiliza como la entrada de otro son muy frecuentes en la prí¡ctica. Por ejemplo, supongamos que deseamos obtener un listado ordenado alfabíticamente con informaciín acerca de todos los usuarios conectados actualmente en la mí¡quina. Sabemos que el comando who permite obtener la informaciín acerca de los usuarios conectados y que sort puede ordenarla alfabíticamente. Una posible soluciín es:

Código: php
$ who > tmp
$ sort < tmp
usuario1 :0 Sep 09 19:53
usuario2 :1 Sep 09 15:45
$ rm tmp


En este caso hemos usado el fichero tmp para guardar de forma temporal la informaciín proporcionada por who antes de usar el comando sort. Una forma simplificada de hacer esto es usar tuberí­as, tal y como se muestra a continuaciín:

Código: php
$ who | sort
usuario1 :0 Sep 09 19:53
usuario2 :1 Sep 09 15:45


El efecto es similar a la creaciín de un archivo temporal, con la ínica diferencia de que el uso de las tuberí­as es mucho mí¡s eficiente.

Uno de los problemas de las tuberí­as es que el usuario no puede observar cuí¡l es la informaciín que corre a travís de ellas. Para conseguirlo se puede usar el comando tee f que toma su entrada estí¡ndar y la vuelca en un fichero de nombre f al mismo tiempo que en su salida estí¡ndar. De esta forma, el comando:

Código: php
$ who | tee who.txt | sort
usuario1 :0 Sep 09 19:53
usuario2 :1 Sep 09 15:45


muestra efectivamente la informaciín ordenada alfabíticamente acerca de los usuarios conectados, pero tambiín la vuelca el listado (sin ordenar) en el fichero who.txt.

Código: php
$ cat who.txt
usuario2 :1 Sep 09 15:45
usuario1 :0 Sep 09 19:53


6.Sustituciín de comandos por su salida

Las tuberí­as nos proporcionan un mecanismo sencillo para utilizar la salida de un comando como entrada de otro. Pero existen multitud de ocasiones en las que lo que realmente necesitamos es usar la salida de uno o varios comandos como parí¡metros para ejecutar otro comando, no como su entrada estí¡ndar.

En estos casos es posible utilizar la tícnica conocida como sustituciín de un comando por su salida. Esto se consigue ejecutando el comando entre tildes graves (`) y toda la informaciín que muestre en su salida estí¡ndar sustituirí¡ a la cadena entre las tildes graves.

Por ejemplo:
Código: php
$ echo La fecha de hoy es ‘date‘
La fecha de hoy es vie sep 09 20:08:54 CEST 2005


El comando date se ha ejecutado entre tildes graves, por lo que todo aquello que produzca en su salida estí¡ndar es capturado y colocado en su lugar.

Es posible combinar tantas sustituciones de comando como sean precisas. Por ejemplo, el siguiente comando muestra la fecha actual y el nímero de usuarios conectados en la mí¡quina.

Código: php
$ echo Hoy es ‘date‘ y hay ‘who | wc l‘ usuarios
Hoy es vie sep 09 20:08:54 CEST 2005 y hay 2 usuarios


Este tipo de sustituciones resultan muy potentes y ítiles en multitud de ocasiones. Por ejemplo, si quisiíramos editar todos aquellos ficheros .txt que contengan la palabra fecha bastarí­a con ejecutar el siguiente comando:

Código: php
$ vi ‘grep l fecha *.txt‘


grep l fecha *.txt muestra en su salida estí¡ndar el nombre de todos aquellos ficheros en el directorio actual terminados en .txt que contienen la cadena de caracteres fecha. Por lo tanto, escribir este comando es equivalente a escribir detrí¡s de vi los nombres de todos esos ficheros.

7.Secuencias de comandos

Para ejecutar varios comandos uno detrí¡s de otro, la forma mí¡s sencilla es escribirlos en varias lí­neas, introduciendo un retorno de carro al final de cada uno. Pero existen situaciones en las que tenemos que escribir necesariamente dos comandos en una misma lí­nea.

Por ejemplo supongamos que el comando morosos produce un listado con las direcciones de correo electrínico de todos los morosos de nuestra empresa en el fichero morosos.txt. Si deseamos enviar una carta a todos ellos podemos usar la siguiente sustituciín de comando:

Código: php
$ morosos
$ mail ‘cat morosos.txt‘ < carta


morosos no produce la lista de morosos en su salida estí¡ndar, sino en el fichero morosos.txt, por lo que una vez ejecutado el comando es necesario usar cat para obtener las direcciones de la lista. Esto exige ejecutar en secuencia dos comandos y para ello podemos usar el punto y coma como separador en vez de teclear dos lí­neas.

Código: php
$ morosos; mail ‘cat morosos.txt‘ < carta


Pero pensemos por un momento en que el comando morosos falla por cualquier razín y no produce el fichero morosos.txt. En este caso el comando cat fallarí­a tambiín al no encontrarlo o bien leerí­a un ficheros de morosos resultado de una ejecuciín previa del comando. Es evidente que tan sílo debe enviarse la carta si el programa de morosos se ha ejecutado correctamente.

En estos casos podemos usar la composiciín secuencial de comandos con el sí­mbolo &&. cmd1 && cmd2 && … && cmdn ejecuta en secuencia los comandos indicados hasta que falle alguno de ellos. En ese caso los comandos que vienen a continuaciín no se ejecutan.

De esta forma, el comando que buscí¡bamos para enviar nuestras cartas a los morosos serí­a el siguiente:

Código: php
$ morosos && mail ‘cat morosos.txt‘ < carta


Otra posibilidad para ejecutar comandos en secuencia es separí¡ndolos con el sí­mbolo ||. cmd1|| cmd2 || … || cmdn ejecuta los comandos especificados hasta que uno de ellos no falle. De esta forma si queremos mostrar un mensaje de error en caso de que no se haya podido enviar la carta a todos los morosos, podemos usar el siguiente comando:

Código: php
$ ( morosos && mail ‘cat morosos.txt‘ < carta ) || echo Error


Fí­jese en que hemos utilizado paríntesis para agrupar los comandos delante del sí­mbolo ||. Estos hacen que los comandos en su interior se agrupen y se ejecuten como si de un ínico comando se tratase. De esta forma si cualquiera de los dos falla aparecerí¡ en pantalla el mensaje Error.

8.Expresiones

Bash shell proporciona un rudimentario lenguaje de expresiones con el que podemos llevar a cabo las operaciones aritmíticas, lígicas o de cadena mí¡s comunes. En esta secciín las estudiaremos todas empezando por las mí¡s sencillas de todas, las variables.

   8.1 Variables

Al igual que cualquier otro lenguaje de programaciín, los programas escritos en bash shell pueden usar variables para almacenar informaciín de forma temporal. La ínica diferencia entre estas variables y las de cualquier otro lenguaje de programaciín de uso habitual es que todas se tratan como si de cadenas de caracteres se tratase y ademí¡s pueden contener datos de longitud arbitraria. Esto significa que aunque una variable contenga el valor 123, el shell interpretarí¡ ese valor como una cadena de tres caracteres, nunca como un nímero entero.

Existen dos tipos principales de variables, las de entorno y las locales. No existe diferencia alguna entre ellas desde el punto de vista de su uso. La ínica diferencia es que cuando un shell invoca a otro, las primeras son heredadas de forma automí¡tica por el shell hijo, mientras que las del segundo grupo no. Por ejemplo, suponga que en su shell de trabajo actual las variables PATH y PWD son variables de entorno y L1 y L2 son variables locales.

El bash shell define automí¡ticamente algunas variables de entorno que son de gran interís. A continuaciín se muestran las mí¡s importantes:

HOME: Nombre del directorio con la cuenta del usuario.
PATH: Un conjunto de nombres de directorio separados por el sí­mbolo : en los que buscar los comandos.
LOGNAME: Nombre del usuario.
SHELL: Nombre completo del shell que se estí¡ utilizando.
TERM: Tipo de terminal que se estí¡ utilizando.

Tambiín define de forma automí¡tica diversas variables locales que toman valores por defecto cada vez que se ejecuta una copia del shell La siguiente tabla recoge las mí¡s importantes:

PPID: Nímero identificador del proceso padre.
PWD: Nombre del directorio de trabajo actual.
OLDPWD: Nombre del anterior directorio de trabajo antes de ejecutar por íltima vez el comando cd.
RANDOM: Un nímero entero generado al azar.
PS1: Cadena que presenta el shell cada vez que solicita un comando.
PS2: Cadena que presenta el shell cada vez que solicita la continuaciín de un comando que ocupa varias lí­neas de pantalla.

   8.1.1 Definiciín y consulta

En bash shell no es necesario declarar las variables. Para introducir una nueva tan sílo es necesario asignarle un valor utilizando la siguiente sintaxis (fí­jese en que no existe ningín espacio en blanco al rededor del signo =):

Código: php
$ mi_nombre=juan
$ mi_maquina=bicho.es


Para la consulta de variables es necesario preceder el nombre de las variables del signo $. Por ejemplo:

Código: php
$ mi_direccion=$mi_nombre@$mi_maquina
$ mi_cuenta=file://$mi_maquina/$LOGNAME
$ echo $mi_direccion
juan@bicho.es
$ echo mi_direccion
mi_direccion
$ mi_otra_direccion=mi_nombre@mi_maquina
$echo $mi_otra_direccion
mi_nombre@mi_maquina


Todas las variables que definamos usando este mítodo son variables locales, y por lo tanto no son heredadas por los shells que se ejecuten a partir del actual. Para definir una variable de entorno se debe usar el comando export. Por ejemplo:

Código: php
$ export mi_direccion=$mi_nombre@$mi_maquina


crea una variable de entorno llamada mi_direccion que serí¡ heredada por cualquier subshell. Cualquier variable local puede convertirse en variable de entorno usando el comando export variable. Para ver la lista de todas las variables de entorno se puede usar el comando export sin ningín parí¡metro.

   8.1.2 Consulta avanzada de variables

Anteponer un $ al nombre de una variable es el modo de consulta mí¡s sencillo para las variables, pero existen otras mí¡s complejos que se recogen en la siguiente tabla:



A continuaciín se muestra un ejemplo de uso de todos estos modos avanzados de acceso a las variables.

Código: php
$ mi_nombre=juan
$ mi_maquina=bicho.es
$ mi_direccion=$mi_nombre@$mi_maquina
$ echo ${#mi_nombre}
4
$ cd trash
$ echo $PWD ${#PWD}
/home/juan/trash 16
$ echo $HOME
/home/juan
$ echo ${PWD#$HOME/}
trash
$ ls
f.dat
$ mi_fichero=f.dat
$ cp $mi_fichero ${mi_fichero%.dat}.bak
$ ls
f.dat f.bak


   8.1.3 Vectores

Bash shell ofrece la posibilidad de usar vectores de valores utilizando una sintaxis con corchetes para los subí­ndices. La ínica limitaciín en este caso es que el í­ndice de los mismos debe estar entre 0 y 511, por lo que tan sílo son posibles vectores de hasta 512 componentes.

Para crear un vector basta con asignar un valor a uno de sus componentes. Los restantes se irí¡n creando de forma diní¡mica conforme se vayan necesitando. Para acceder a una componente particular se usa la sintaxis ${v}. Por ejemplo:

Código: php
$ amigos[0]=Juan
$ amigos[1]=Luí­s
$ amigos[2]=Marí­a
$ amigos[3]=Ana
$ echo ${amigos[0]} estí¡ casado con ${amigos[2]}
Juan estí¡ casado con Marí­a
$ echo Tengo ${#amigos[*]} en la agenda
Tengo 4 amigos en la agenda
$ Mis amigos son ${amigos[*]}
Mis amigos son Juan Luí­s Marí­a Ana


La notaciín ${v
  • } se utiliza para acceder a todos los elementos del vector de forma conjunta.

       8.2 Variables especiales

    El bash shell permite el uso de algunas variables especiales que se recogen en la siguiente tabla. Su valor no se puede cambiar de la forma que hemos visto hasta ahora y generalmente sílo se utilizan en los programas escritos en shell script, nunca cuando el shell se utiliza de forma interactiva.


    Todas las variables que hemos mostrado son especiales en el sentido de que no es posible darles un valor utilizando los mítodos que hemos estudiado hasta el momento. De todas formas las variables de la forma $i son una excepciín, ya que es posible cambiar sus valores a voluntad, aunque utilizando el comando set y no los mecanismos habituales.

    Cuando set se ejecuta con parí¡metros, lo que hace es asignarlos a las variables $0, $1, $2, etcítera. Por ejemplo, a veces suele ser preciso determinar la hora actual a partir del comando date. El problema es que este comando muestra en su salida estí¡ndar una cadena que informa no sílo de la hora actual sino tambiín del dí­a de la semana, el mes, el aíño etcítera.

    Código: php
    $ date
    lun sep 12 13:08:54 CEST 2005


    Para obtener sílo la hora podrí­amos ejecutar el siguiente comando:

    Código: php
    $ set ‘date‘
    $ echo $4


    Al ejecutar date entre tildes graves, su salida se convierte en los parí¡metros del comando set, por lo que el comando anterior es equivalente a teclear:

    Código: php
     $ set lun sep 12 13:11:06 CEST 2005


    Una vez se ha ejecutado, las variables $0, $1, … reciben los valores lun, sep, … Por lo tanto $4 contendrí¡ una cadena que representa la hora actual.

       8.3 Expresiones aritmíticas

    Bash shell ofrece a los usuarios la posibilidad de realizar operaciones aritmíticas sencillas mediante el uso del comando let. Este comando aplicado sobre una expresiín aritmítica devuelve como cídigo de retorno el resultado de evaluarla. En la expresiín se pueden usar los operadores que se recogen en la siguiente
    tabla, agrupados en orden descendente de prioridad:


    Todos estos operadores, excepto el de asignaciín, son asociativos por la izquierda. Las expresiones pueden usar paríntesis para modificar el orden de evaluaciín, pero en ningín momento se comprueba si alguna de ellas produce desbordamiento.

    A continuaciín se muestran varios ejemplos (Fí­jese en que no hay ningín espacio en blanco alrededor del signo =, ni tampoco alrededor de los operadores):

    Código: php
    $ let x=2+3
    $ echo $x
    5
    $ let y=x*5
    $ echo $y
    25


    El ínico punto de interís es que dentro de las expresiones del comando let las variables, pese a que se estí¡ consultando su valor, no es necesario preceder su nombre del signo $. En cualquier caso, podemos hacerlo si así­ lo deseamos, como se muestra a continuaciín:

    Código: php
    $ let x=2+3
    $ echo $x
    5
    $ let y=$x*5
    $ echo $y
    25


    Generalmente no existe ningín problema con la mayorí­a de las expresiones, salvo en aquellas en las que aparecen los operadores *, < o >, puesto que estos caracteres, como ya sabemos, tienen un significado especial para el shell. Para evitar que el shell los interprete de una forma especial, lo mejor es escribir la expresiín entre comillas o bien usar la notaciín

    Código: php
    (( expresiín ))


    que es casi equivalente a escribir

    Código: php
    let " expresiín "


    La ínica diferencia es que al usar los dobles paríntesis se permite usar espacios en blanco alrededor de todos los operandos y operadores.

    Código: php
    $ (( x = 2 + 3 ))
    $ echo $x
    5
    $ (( y = $x * 5 ))
    $ echo $y
    25


       8.4 El comando test

    El comando test nos permite llevar a cabo operaciones lígicas sencillas con ficheros, cadenas y nímeros enteros. El formato de este comando es test expresion.. O de forma equivalente

    Código: php
    [ expresion ]


    La expresiín puede tener cualquiera de los formatos que se muestran en la siguiente tabla:


    9.Rutinas

    El bash shell permite definir bibliotecas de rutinas de utilidad que pueden ser compartidas por multitud de shell scripts. Para escribir una rutina se utiliza la siguiente sintaxis:

    Código: php
    nombre_rutina ()
    {
    lista de comandos
    }


    Para llamar a una rutina basta con escribir su nombre seguida de los parí¡metros adecuados, exactamente igual que si estuviísemos ejecutando cualquier otro comando. En el siguiente ejemplo se muestra una sencilla rutina que muestra un mensaje de saludo. Por conveniencia, conviene escribir todas las rutinas en ficheros a los que es preciso dar permiso de ejecuciín utilizando el comando chmod.

    Código: php
    $ cat > saludo.bsh
    #!/bin/bash
    saludo ()
    {
    echo ¡Hola $1!
    }
    # Principal
    saludo $LOGNAME
    saludo amigo $LOGNAME
    saludo
    ^D
    $ chmod ugo+x saludo.bsh


    La rutina saludo se limita a mostrar en pantalla un mensaje dando la bienvenida a la persona cuyo nombre se le pasa como primer parí¡metro. No es necesario declarar la rutina de una forma especial para indicar que admite parí¡metros y tampoco se realiza ningín tipo de comprobaciín en cuanto al nímero de parí¡metros reales que se utilizan en la llamada.

    Para acceder al parí¡metro iâ€" ísimo de una rutina se usa la notaciín $i, pero si en la llamada ese parí¡metro no ha sido suministrado entonces se sustituye por una cadena vací­a. De esta forma, si ejecutamos el anterior shell script obtendremos la salida que se muestra a continuaciín:

    Código: php
    $ saludo.bsh
    ¡Hola juan!
    ¡Hola amigo!
    ¡Hola !


    Fí­jese en que en la segunda llamada hemos usado dos parí¡metros y en la tercera ninguno. En el primer caso el parí¡metro extra es ignorado y en el segundo es sustituido por una cadena vací­a.

       9.1Algo mí¡s sobre parí¡metros

    Desgraciadamente, el mecanismo de variables $i tan sílo nos permite acceder a los nueve primeros parí¡metros que se le pasen a la rutina. Si es preciso usar mí¡s parí¡metros dentro de una rutina tenemos que recurrir al comando shift. Cada vez que se ejecuta desplaza los parí¡metros que se han pasado a una rutina una posiciín
    a la izquierda, de forma que el primero se pierde, el segundo se convierte en el primero y así­ sucesivamente. El siguiente ejemplo muestra la forma en que funciona este comando:

    Código: php
    $ cat > shift.bsh
    #!/bin/bash
    desplaza ()
    {
    echo $1 $2 $3
    shift
    echo $1 $2 $3
    shift
    echo $1 $2 $3
    shift
    }
    # Programa principal
    desplaza a b c
    ^D
    $ chmod ugo+x shift.bsh
    $ shift.bsh
    a b c
    b c
    c


    Para acceder a la lista completa de parí¡metros podemos usar la variable especial $*, que se equivale a una cadena con todos los parí¡metros que se han pasado a la rutina separados por espacios en blanco.

    Para terminar este estudio que estamos realizando de los parí¡metros, pruebe el siguiente shell script:

    Código: php
    $ cat > curioso.bsh
    #!/bin/bash
    mas_curioso ()
    {
    echo curioso ha recibido $# parí¡metros
    }
    curioso ()
    {
    echo curioso ha recibido $# parí¡metros
    mas_curioso $*
    }
    # Programa principal
    curioso "hola $LOGNAME"
    ^D
    $ chmod ugo+x curioso.bsh


    Si lo ejecuta verí¡ que la salida es la siguiente:

    Código: php
    $ curioso.bsh
    curioso ha recibido 1 parí¡metros
    mas_curioso ha recibido 2 parí¡metros


    ¿Címo es posible que si a la rutina curioso se le pasa un parí¡metro y lo utiliza para llamar a mas_curioso, esta rutina reciba dos? La razín es que el parí¡metro que se le ha pasado a curioso es una cadena con un espacio en blanco y el comando

    Código: php
    mas_curioso $*


    se expande como:

    Código: php
    mas_curioso hola juan


    Al no haber comillas alrededor del parí¡metro en esta llamada, el shell interpreta que mas_curioso debe recibir dos parí¡metros: uno con la cadena hola y otro con la cadena juan. Encerrar la variable $* entre comillas tampoco sirve de mucho, ya que en este caso si pasamos mí¡s de un parí¡metro a la rutina curioso, entonces
    mas_curioso sílo recibirí¡ uno.

    El problema de los espacios es quizí¡ uno de los mí¡s importantes cuando trabajamos con shell scripts, puesto que suelen ser una fuente importante de errores. Desgraciadamente no existe ninguna soluciín simple.

       9.2Valor de retorno

    Las rutinas escritas en bash shell j pueden devolver valores de retorno enteros mediante el uso del comando return. Pese a que este comando se puede utilizar en cualquier punto de la rutina, provocando su terminaciín inmediata, lo mí¡s adecuado es usarlo siempre al final. Para determinar cuí¡l es el valor de retorno de cualquier
    rutina se puede usar la variable especial $?.

    A continuaciín se muestra un ejemplo de una rutina que calcula y devuelve la suma de los dos nímeros que se le pasan.

    Código: php
    $ cat > suma.bsh
    #!/bin/bash
    suma ()
    {
    (( result = $1 + $2 ))
    return $result
    }
    # Programa principal
    suma 1 2
    echo "1 + 2 = $?"
    ^D
    $ chmod ugo+x suma.bsh
    $ suma.bsh
    1 + 2 = 3


    La variable $? devuelve realmente el cídigo de salida del íltimo comando ejecutado, y una llamada a rutina no es mí¡s que un caso particular de ejecuciín de un comando. Esto significa que si deseamos utilizar en varias ocasiones el valor de retorno de una rutina, necesariamente deberemos guardarlo en una variable auxiliar. Por ejemplo, el siguiente cídigo:

    Código: php
    $ cat > suma.bsh
    …
    suma 1 2
    echo "1 + 2 = $?"
    echo "2 + 1 = $?"


    produce al ejecutarse la salida siguiente:

    Código: php
    $ suma.bsh
    1 + 2 = 3
    2 + 1 = 0


    La razín es que cuando se usa $? por primera vez devuelve el resultado de haber ejecutado la llamada suma 1 2. En cambio cuando se usa por segunda vez devuelve el cídigo de salida del comando anterior, esto es, de echo "1 + 2 = $?". Como este comando se ha ejecutado con íxito, la segunda vez que se consulta la variable $? devuelve el cídigo de error 0 indicativo de que el íltimo comando se ejecutí adecuadamente.

    Mediante el comando return tan sílo es posible devolver valores enteros. Si deseamos devolver texto entonces tenemos que usar el comando echo para escribirlo en la salida estí¡ndar de la funciín y realizar una sustituciín de comando para capturar su salida. A continuaciín mostramos un ejemplo en el que la rutina saludo devuelve un mensaje saludando al usuario que la ejecuta.

    Código: php
    saludo ()
    {
    echo "Hola $LOGNAME."
    echo ‘date‘
    }
    msg=‘saludo‘
    echo $msg


       9.3Variables locales

    Dentro de una rutina todas las variables que se definan son globales por defecto, esto es, son visibles no sílo dentro del í¡mbito de la rutina sino de todo el shell script en el que aparecen. Para introducir una variable de í¡mbito local se utiliza el comando typeset seguido del nombre de la variable. Cuando la definamos posteriormente tan sílo serí¡ conocida dentro de la rutina en que ha sido declarada. En el siguiente ejemplo se ilustra el uso de este comando.

    Código: php
    $ cat > typeset.bsh
    #!/bin/bash
    locales ()
    {
    typeset x
    (( x = $1 + $2 ))
    echo local: x = $x
    return $x
    }
    # programa principal
    x=$LOGNAME
    echo global: x = $x
    locales 1 2
    echo global: x = $x
    ^D
    $ chmod ugo+x typeset.bsh
    $ typeset.bsh
    global: x = juan
    local: x = 3
    global: x = juan


       9.4Biblioteca de rutinas

    En muchas ocasiones las rutinas que escribimos para un programa podrí­an usarse en otros muchos. El bash shell nos proporciona un sencillo mecanismo para agrupar rutinas en forma de bibliotecas.

    Para construir una biblioteca tan sílo tiene que teclear las rutinas que la componen en un fichero, generalmente con la extensiín .bsh, que debe encontrarse en alguno de los directorios que indica la variable $PATH. Para cargar la biblioteca y poder hacer uso de sus rutinas debemos usar un comando especial llamado ".". No, no crea que es una errata. El nombre del comando es un punto. Por ejemplo:

    Código: php
    $ . saludo.bash
    ¡Hola pepe!
    ¡Hola amigo!
    ¡Hola !
    $ saludo Juan
    ¡Hola Juan!
    $ _


    Todos los comandos que no aparezcan dentro de una rutina en una biblioteca se ejecutan al cargarla. De ahí­ que en el ejemplo anterior apareciesen los tres saludos iniciales.

    A partir de ahora, en todos los ejercicios usaremos una biblioteca muy ítil que nos permitirí¡ mostrar mensajes de aviso o de error. Mostramos su cídigo a continuaciín:

    Código: php
    #!/bin/bsh
    # error.bash
    error ()
    {
    echo "$*" 1>&2
    exit 1
    }
    warning ()
    {
    echo "$*" 1>&2
    }