4.6.- Escritura y ejecución de un script en shell

Un script shell es un archivo de texto que contiene los comandos Unix/Linux internos o externos así como las palabras claves del shell.

Cuando programamos en C, creamos un fichero con extensión .c donde escribimos el código del programa y a continuación lo compilamos para obtener el programa ejecutable. De igual forma, en Bash, la forma más común de ejecutar programas es crear ficheros (en este caso con extensión .sh) que se llaman archivos interpretados o scripts y que contienen una colección de órdenes Bash que serán ejecutadas por la shell.

Podemos usar nuestro editor favorito, o directamente desde la consola si se trata de pequeños scripts.

Para implementar este mecanismo, los diseñadores de LINUX sobrecargaron la funcionalidad de la llamada al sistema que permite ejecutar un programa, la llamada exec. De esta forma, cuando mandamos a ejecutar un programa, la función exec comprueba si el archivo es binario o no. Si es un binario, resultado de una compilación, lo ejecuta en la forma usual. Pero si el archivo no es binario, la función exec está diseñada para suponer que lo que contiene el archivo son órdenes para un intérprete de órdenes, por lo que ejecuta un shell y le pasa el contenido del archivo para que lo interprete. De aquí el nombre de archivos interpretados. Ahora bien, el shell creado por exec por defecto es un Bourne shell (bash) ¿Qué ocurre si queremos ejecutar un programa escrito en otro lenguaje shell?

En este caso se utiliza el siguiente convenio: la primera línea del archivo interpretado debe contener el programa intérprete al que se le debe pasar el resto del contenido del archivo. El convenio para indicar el intérprete es:

De esta manera, podemos ejecutar programas shell de una manera sencilla. Esta indicación
debe ser siempre la primera línea del script y no puede tener espacios en blanco.

Primeros pasos con scripts

Para crear un script usamos cualquier editor de texto y lo guardamos en un fichero. Una vez
creado el script existen dos formas de ejecutarlo:

  1. La primera forma implica dar al fichero permiso de ejecución (con el comando chmod). Una vez dado este permiso, podremos ejecutarlo simplemente poniendo ./script. De hecho, muchos comandos comunes (p.e. spell o man) están implementados como scripts Bash y no como programas C ejecutables.
  2. La segunda consiste en ejecutarlo con el comando source, el cual carga el fichero en la memoria de Bash y lo ejecuta.

Una diferencia entre estas dos formas de ejecutar un script es que si lo ejecutamos con source, éste se ejecuta dentro del proceso del shell, mientras que si lo ejecutamos como un fichero ejecutable se ejecuta en un subshell. Esto implica que sólo las variables de entorno exportadas son conocidas por el nuevo subshell que ejecuta el programa; y que las variables que sean creadas en la ejecución del programa se conocerán a su finalización.

Nuestro primer script : Hola mundo

Siguiendo la tradición “centenaria” escribiremos el típico primer ejemplo de un script (valido para cualquier lenguaje de programación), nuestro tradicional y original “Hola mundo“.

Para crear un script basta con abrir un fichero con cualquier editor de texto (emacs, nedit, pico, vi, gedit, cat, etc…,) y, a continuación, indicar las acciones que queramos que realice nuestro script.

Si sale algo como ./saludo.sh: Comando desconocido, probablemente la primera línea, ‘#!/bin/bash‘, esté mal escrita. Ejecutad whereis bash para saber donde se encuentra el comando bash y aseguraos de que las barras son las adecuadas y que no habéis dejado espacios.

Comentarios en los scripts

En un script todo lo que vaya después del símbolo # y hasta el siguiente carácter de nueva línea se toma como comentario y no se ejecuta.

Los scripts suelen comenzar (después de la línea #!/bin/bash) con comentarios que indican el nombre del archivo y lo que hace el script. También se suelen colocar comentarios de documentación en diferentes partes del script para mejorar la comprensión y facilitar el mantenimiento. Un caso especial es el uso de # en la primera línea para indicar el intérprete con que se ejecutará el script. Un script con comentarios quedaría así:

El resultado de este script sería:

 Este script (misdatos.sh) muestra por pantalla una serie de datos sobre el usuario. Para ello, utiliza variables de entorno.

Variable en los scripts

Igual que cuando programamos en C, dentro de los scripts se pueden utilizar variables para clarificar o facilitar la compresión del programa. Si no se especifica otra cosa, las variables Bash son de tipo cadena, por lo que pueden contener un número (representado como una cadena), un carácter o una cadena de caracteres.No es necesario declararla, se creará sólo con asignarle un valor a su referencia.

El script variables.sh crea una variable cad con el valor “Hola mundo” y a continuación la muestra por pantalla:

 El script de arriba daría como resultado lo siguiente:

Al asignar una cadena que contiene espacios a una variable, sin utilizar comillas, nos da un error.

Paso de argumentos a los scripts

En ocasiones, puede ser útil que nuestros scripts reciban algún tipo de argumento (un directorio sobre el que actuar o un tipo de archivo que buscar) al ser ejecutados. Para hacer referencia a estos argumentos, dentro de los scripts se emplean una serie de variables que siempre estarán disponibles, los parámetros posicionales $1, $2, $3, , , siendo $0 el nombre del propio programa. Reciben este nombre porque se les reconoce por su ubicación, es decir el primer argumento es $1, el segundo $2 y así sucesivamente.

El resultado sería (sin argumento: ./argumentos.sh; con argumentos: ./argumentos.sh 1 5):

 Si el argumento posicional al que aludimos no se pasa como argumento, entonces tiene el valor nulo.

Resultado del script:

Vemos que ejecutando el script con sólo dos argumentos, los muestra sin problema anulando los otros dos que no hemos introducido.

Un último ejemplo:

Resultado:

Las variables $*, $@ y $#

La variable de entorno $# almacena el número total de argumentos o parámetros recibidos por el script sin contar al $0. El valor es de tipo cadena de caracteres, pero más adelante veremos como podemos convertir este valor a número para operar con él. La variables $* y $@ contienen, ambas, los valores de todos los argumentos recibidos por el script.

Por ejemplo:

Como ejemplo, el script siguiente lo vamos a modificar tal como muestra el listado de abajo para que use estas variables y sacar los argumentos recibidos.

Y su resultado:

Aunque cuando no entrecomillamos $* o $@ no existen diferencias entre usar uno y otro, cuando los encerramos entre comillas dobles (“”), podemos cambiar el símbolo que usa $* para separar los argumentos cuando los muestra por pantalla. Por defecto, cuando se imprimen los argumentos, estos se separan por un espacio, pero podemos utilizar otro separador indicándolo en la variable de entorno IFS(Internal Field Separator – Separador de Campo Interno). La variable $@ siempre usa como separador un espacio y no se puede cambiar. Por ejemplo si hacemos el siguiente script:

Resultado del script:

Observamos que al cambiar la variable IFS del listado “IFS=’,’” la primera línea aparece como separador la coma “,“. Prueba con otros caracteres que hagan de separadores. Siempre conviene entrecomillar las variables $* y $@ para evitar que los argumentos que contengan espacios sean mal interpretados.

Expandiendo las variables usando llaves

Realmente la forma que usamos para ver acceder al contenido de una variable, $variable, es una simplificación de la forma más general ${variable}. La simplificación se puede usar siempre que no existan ambigüedades. En este apartado veremos cuando se producen las ambigüedades que nos obligan a usar la forma ${variable}.

La forma ${variable} se usa siempre que la variable va seguida por una letra, dígito o guión bajo (_); en caso contrario, podemos usar la forma simplificada $variable. Por ejemplo, si queremos escribir nuestro nombre (almacenado en la variable nombre) y nuestro apellido (almacenado en la variable apellido) separados por un guión podríamos pensar en hacer:

Pero esto produce una salida incorrecta porque Bash ha intentado buscar la variable $nombre_ que al no existir la ha expandido por una cadena vacía. Para solucionarlo podemos usar las llaves:

Otro lugar donde necesitamos usar llaves es cuando vamos a sacar el décimo parámetro posicional. Si usamos $10, Bash lo expandirá por $1 seguido de un 0, mientras que si usamos ${10} Bash nos dará el décimo parámetro posicional También necesitamos usar las llaves cuando queremos acceder a los elementos de una variable multi-palabra. Por ejemplo:

 El comando shift

el comando shift permite mover la lista de argumentos una o varias posiciones hacia la izquierda.

Sintaxis

donde n representa el valor del movimiento (n vale 1 por defecto).

UN ejemplo práctico: