educación, informática y demás

24.13 - Repaso general

Funciones

Las funciones nos permiten reutilizar código. Podríamos ver una función cómo un conjunto de comandos que realizan, todos ellos, una tarea concreta a la que le damos un nombre o identificador.

Veamos un ejemplo

Si ejecutamos el script…

El script no hace nada, porque para que una función se ejecute hay que llamarla o invocarla.

Ahora vamos a ejecutarlo

La ventaja de una función es que podemos llamarla tantas veces cómo necesitemos en nuestro programa o script.

Estamos ejecutando la función tres veces, llamándola en la línea 17, 18 y 19. Esto hará que el script ejecute el cuerpo de la función, que es el código que está incluido entre las llaves de apertura y cierre, tres veces.

Parámetros

Al igual que con los scripts, podemos especializar la tarea que realiza una función gracias al uso de parámetros. Podemos pasarle valores por parámetro a una función y utilizarlos dentro.

Para pasar los valores, hacemos como cuando pasamos valores por parámetro a un script, es decir los escribimos a continuación separados por espacios.

Para acceder a los valores pasados por parámetro en una función, utilizamos las mismas variables especiales que usamos para acceder a los valores pasados por parámetro en un script. Esto es: $1 .. $9, $#, $@, $* …

Ámbito de las variables

Pero, cómo distinguimos entre $1 pasado al script y $1 pasado a la función. Aquí entra en juego el ámbito de las variables.

  • Si accedemos a $1 dentro de una función estaremos haciendo referencia al valor pasado en el primer parámetro de la función.
  • Si accedemos a $1 fuera de una función, estaremos haciendo referencia la valor pasado en el primer parámetro del script

Por eso, y por otros motivos, es importante asignar los valores de los parámetros pasados a un script con los que vayamos a trabajar a variables. Lo mismo es aplicable a las funciones.

Ámbito de las variables

Las variables en los scripts de bash son globales. Es decir, si asignamos un valor a una variable dentro de una función, este nuevo valor se asigna para todo el script.

Si queremos tener una variable local de la función, es decir que tenga un valor concreto dentro de la función tendremos que utilizar la palabra clave local antes de asignarle cualquier valor a la variable.

El problema está en que si utilizamos una variable fuera de la función con un nombre, por ejemplo ruta, y dentro de una función cambiamos el valor de dicha variable, ese cambio afecta a todo el script. Es más, la variable ruta estará disponible dentro de la función, se trata de variables globales.

Seguimos con el ejemplo…

Ejecutamos la función mensaje pasándole por parámetro tres rutas distintas en cada ejecución.

Veamos el resultado de ejecutar el script

No hay cambios en la salida… :_(

Le estamos pasando un valor por parámetro, pero el código de la función no hace uso de él. Vamos a modificar la función.

De esta forma, estamos reutilizando la funcionalidad de la función, valga la redundancia, pero la función proporciona información diferente cada vez que la ejecutamos puesto que utiliza el valor del primer parámetro que se le ha pasado para especializar la tarea que realiza: proporcionar información sobre un directorio cuya ruta se pasa por parámetro.

Nos muestra ciertos problemas con permisos de acceso, ¿cómo podemos solucionarlo?

La solución no está en el script, sino en cómo lo ejecutamos: sudo.

Vamos a ver si entendemos lo del ámbito de las funciones 🙂

¿Qué mostrará la línea 21 en pantalla?. Aunque si miramos el código de las líneas 17 a la 21 la respuesta lógica sería que la línea 21 mostraría «El valor de dir es /home» esta variable, dir, ha sido modificada dentro de la función mensaje. Cómo no hemos utilizado la palabra clave local, al asignar un valor a la variable dir, se está haciendo referencia a una variable global.

Tenemos que tener presente esta situación y utilizarla como mejor nos convenga:

  1. Utilizar variables globales que sabemos que pueden ser accedidas para lectura y escritura, si lo necesitamos, desde las funciones de nuestro script. Esto puede generar problemas si no somo cuidadosos.
  2. Utilizar variables globales con nombres distintos. En el ejemplo, la variable global fuera de la función podríamos llamarla rutaDir y la variable dentro de la función dir. De esta forma no hay colisión entre las variables.
  3. Utilizar la palabra reservada local. De esta forma, aunque las variables se llamen igual, cuando se haga referencia a la variable dir dentro de la función, el shell bash utilizará la varible local a la función, no el valor de la varible global.

Vamos a probar la tercera aproximación.

Veamos el resultado

Aunque las variables se llamen igual, al utilizar la palabra reservada local en la asignación de la variable dir dentro de la función, estamos indicando que esa variable es local a la función y que se accederá a ella, y no a la variable global, dentro de esta función.

Etapa 1 – Ruta del directorio por parámetro del script

Modifica el script del ejemplo3.sh de forma que la ruta del directorio para el que vamos a mostrar información se pase por parámetro al script.

Vamos a probar

Etapa 2 – Comprobar que el directorio existe

Modifica la función de forma que si el directorio cuya ruta se pasa por parámetro no existe o no es un directorio se muestre un mensaje indicando la situación. Si el directorio existe, mostraremos información de ocupación del directorio.

Vamos a probarlo…

Etapa 3 – Lista variable de parámetros

Modifica el script de forma que recorra una lista variable de parámetros que se pueden pasar al script. Los parámetros serán rutas de directorios para los que se quiere obtener información de ocupación.

Solo hemos cambiado las líneas de la 24 a la 26. Ahora, si no se pasa parámetro no se muestra nada, pero por lo demás funcionará como esperamos

Etapa 4 – Varias funciones

Crea un par de funciones llamadas ocupacionDisco y ocupacionDirectorio.

La función ocupacionDisco mostrará el mensaje de bienvenida e información de ocupación del sistema de ficheros /. Puedes copiar y pegar el código de la función mensaje.

La función ocupacionDirectorio mostrará información de ocupación del directorio cuya ruta se pasa por parámetro. Puedes copiar y pegar las líneas de la función mensaje que realizan esta operación.

Por último, elimina la función mensaje, puesto que no la utilizaremos más: hemos dividido su funcionalidad en dos funciones distintas.

Ahora modifica el script para que muestre una sola vez la información de ocupación en disco del sistema de ficheros raíz y después, para cada directorio cuya ruta se haya pasado por parámetro al script, muestre información de ocupación del directorio.

La ventaja de esta alternativa es que solo se mostrará información de ocupación del sistema de ficheros raíz una sola vez, no para cada directorio. Esta información era redundante en la versión anterior.

Etapa 5 – Redirección de salida

Queremos almacenar la información de salida de la ocupación de disco y de la ocupación de cada directorio cuya ruta se pasa por parámetro en un fichero de log. Este fichero de log se llamará sizeDir.log y se almacenará en el directorio actual.

Si el fichero existe, se sobrescribe, de forma que después de ejecutar el script solo contenga la información de la última vez que se ha ejecutado el script.

Al redirigir la salida de una llamada a una función estamos redirigiendo la salida de todos los comandos que se ejecuten dentro de esa función.

En la línea 26 usamos redirección simple para que se sobrescriba el fichero $logFile si existe. No obstante, en la línea 28 utilizamos redirección doble, puesto que esta línea se ejecutará una y otra vez en el bucle y la información que proporciona se debe añadir al fichero para que al finalizar el script tengamos toda la información de salida, generada por la ejecución de varias funciones, en un solo fichero de log.

Retorno de una función y obtener valores de una función

Una función puede retornar un código de salida exactamente igual que un script. Este código de salida es un valor numérico entero que indica si la función se ha ejecutado correcamente o si ha tenido algún error.

El código devuelto por defecto es el código de salida de último comando ejecutado por la función.

Vamos a probar con un script llamado ejemplo4.sh

La función cargaFichero devuelve como código de salida el código de salida del último comando ejecutado, en este caso un ls -lh sobre el fichero /etc/passwd.

Que pasará si el comando ls falla…

Veamos la salida…

Veamos el siguiente cambio

Se mostrará el mensaje Todo ha ido correctamente puesto que el valor de retorno de la función es el código de salida del último comando ejecutado, un echo que ha funcionado correctamente.

Si queremos devolver un código de salida de una función utilizamos el comando return seguido del código que queramos devolver

Vamos a probarlo…

Ahora vamos a modificar la función de manera que, si la ruta que se proporciona en el parámetro $1 no es de un fichero o el fichero no existe, entonces terminamos la ejecución de la función retornando un código de error. Sino, mostramos información de permiso propietario y grupo del fichero y su contenido. Además recibiremos la ruta del fichero por parámetro

Modifica el script para que se carguen los ficheros cuya ruta se pasen por parámetro. El script recibirá cero o más parámetros.

Vamos a recordar el bucle for

Entonces… con simple for, tendríamos resuelto el problema.

La función no ha cambiado, hemos quitado la asignación de file=$1 puesto que ya no la vamos a usar.

Ahora, símplemente recorremos cada parámetro pasado asignándoselo a la variable file gracias a un bucle for

Ejemplo5.sh

Copia el código de ejemplo4.sh en el fichero ejemplo5.sh. Después modifica el script ejemplo5.sh para que ahora se solicite la ruta del fichero sobre el que se desea obtener información por teclado. De esta forma, se mostrará un mensaje por pantalla al operador y éste insertará la ruta del fichero a consultar. Si no inserta ningún dato no mostramos nada. Con la ruta proporcionada llamamos a la función.

Ahora modificamos el código

Vamos a ver cómo funciona

Vamos a modificar el código para contar con una función que cargue en la variable file la ruta que el operador inserta por teclado.

Vamos a probarla

La vueltita que le queremos dar ahora es que no tengamos que comprobar si hay algo almacenado en la variable $file fuera de la llamada a la función. Queremos algo así:

Dónde tenemos los ??? deberíamos ejecutar un comando que si todo va bien signifique que $file no está vacío y si va mal signifique que $file está vació.

El último comando que se ejecuta en la función pideRuta es el comando test que comprueba que $file no esté vacío. Si no está vacío el comando test retornará un código de salida 0 o éxito, que será el código que retorne la función. En caso contrario, el código de salida de test será distinto de cero, que será el código de retorno de la función.

Ahora, mientras el operador inserte la ruta de un fichero, cargaremos el fichero con la función cargaFichero y volveremos a solicitar ruta de fichero. Utiliza la nueva función para solicitar nueva ruta de fichero y para comprobar si se pasa algún valor.

Tenemos que utilizar un while 🙂

Solo vamos a cambiar el if por un while, gracias a el cambio que hicimos en la función pideRuta

Vamos a probar si funciona

Ejemplo6.sh

Vamos a crear una función que solicite al usuario un número entre el 1 y el 254 Mientras el usuario inserte un número válido dentro del rango, el script comprobará si hay conexión con el host cuyo número se ha insertado dentro de la red 192.168.40.0/24.

Deberíamos tener en cuenta, como situaciones de error:

  • Que no se proporcione un valor numérico
  • Que el número esté fuera del rango

Comando: ping con la opción -c

Otro Scripts

Probamos

Dejar una respuesta