educación, informática y demás

26.03 - Automatizacion I

Condicionales y otras especies

En esta entrada vamos a jugar un poco con las sentencias condicionales en bash shell script, en concreto con el comando if.

Código de retorno de un comando

Se trata de un valor entero que toda aplicación o comando retorna cuando termina. Este valor no se muestra al usuario, es un código interno que se puede consultar si lo deseamos.

En GNU/Linux bash este código significa lo siguiente:

  • 0 — Todo ha ido bien.
  • Otra cosa — Código de error: algo ha fallado.

Es el programador de la aplicación el que decide qué código de error devuelve y su significado.

A nosotros esto se nos sale de nuestro campo de acción. Simplemente vamos a comprobar el código de retorno de una aplicación cuando todo funciona bien y cuando no.

Vamos a seguir ejecutando pero ahora que falle 🙂

Comando if

El comando if implementa una sentencia condicional dónde la condición es la ejecución de un comando: si el comando se ejecuta correctamente entonces se cumple la condición.

¿Cómo sabe el comando if que se ha ejecutado correctamente el comando? Gracias al código de retorno de dicho comando.

Si no sabemos como funciona un comando, consultamos su ayuda.

La única parte que es obligatoria es el if con las ORDENES (comandos o aplicaciones) que se ejecutan para comprobar si se cumple la condición y la rama del then que contiene las ORDENES que se ejecutarán en caso de cumplirse la condición.

Ejercicios

Es siempre la misma estructura con los if simples:

Crea un script llamado if01.sh que trate de crear el directorio $HOME/web, si se crea correctamente muestra un mensaje indicando que se ha creado.

No está mal, pero estamos usando un mismo literal en dos partes distintas del código :__(

Probamos

Crea un script llamado if02.sh que trate de crear el directorio /home/marinapg/web, si se crea correctamente muestra un mensaje indicando que se ha creado.

Crea un script llamado if03.sh que compruebe si tienes conexión con el host cuya IP es 8.8.8.8. Si hay conexión muestra un mensaje indicando que hay conexión.

Ya tenemos el comando que me permite comprobar si se cumple la condición

Mola, pero no me gusta que muestre información extra en pantalla: puede descolocar al usuario.

Crea un script llamado if04.sh que compruebe si tienes conexión con el host cuya IP es 192.168.2.1. Si NO hay conexión muestra un mensaje indicando que NO hay conexión.

Pista: Para negar una condición, la ejecución correcta de un comando, podemos utilizar el caracter ! previo al comando. Eso sí, recuerda que son comandos y hay que separar con un espacio.

Como tenemos variable tan solo tenemos que cambiar el valor asignado 🙂

Vamos a probarlo 🙂

Crea un script llamado if05.sh que obtenga por parámetro la dirección IP de un host y compruebe si tenemos conexión con el host cuya IP se ha proporcionado en el primer parámetro. Si NO hay conexión muestra un mensaje indicando que NO hay conexión.

Como hemos trabajado con variables es tan sencillo como cambiar el valor literal que estabamos asignado a la variable por el valor del primer parámetro.

Cuidado! cómo no tenemos control de errores nos podemos encontrar en situaciones no deseadas, como por ejemplo si el usuario no pasa parámetro.

Redirigiendo la salida de los comandos

Cuando utilicemos comandos que muestren salida en pantalla para realizar comprobaciones en un if es posible que se muestre en pantalla información que no sea deseable porque puede molestar o confundir al usuario.

En estos casos podemos utilizar la redirección de salida estandar y de error bien a un fichero conocido, si queremos mantener esta información para consultarla más adelante, o bien al fichero de agujero negro o papelera /dev/null. Todo lo que escribamos en este fichero se perderá, como lágrimas en la lluvia.

Además, vamos a redirigir tanto la salida estándar como la salida de error Recuerda las aplicaciones y comandos tienen dos salidas una la estándar, que es la que se utiliza para mostrar información al usuario, y otra la de error, que suele coincidir con la estándar y se muestra también en pantalla para mostrar mensajes solo de error. Tenemos que redirigir ambas si no queremos que aparezcan mensajes no deseados en pantalla.

Para redirigir:

  • Redirección solo de salida CMD > /dev/null
  • Redirección de salida estándar y de error CMD &> /dev/null
  • Redirección solo de salida de error CMD 2> /dev/null

Vamos a modificar algunos de nuestros scripts para que no se muestre información no deseada.

Solo estamos redirigiendo la salida estándar

Vamos a redirigir ambas salidas

Vamos a modificar los scripts

Si ejecutamos ahora no saldrá ningún mensaje de error.

La primera ejecución es la de la primera versión.

Cómo comparar cadenas de texto, enteros, ficheros y directorios

Comando test!

Como sabemos podemos utilizar un comando para realizar comprobaciones: si el comando se ejecuta correctamente se cumple la condición y se ejecutan las ordenes (comandos o aplicaciones) que estén en la rama del then.

No obstante, como ya hemos podido comprobar si hemos realizado algun programa en LPAN como python o php, es muy habitual tener que realizar comprobaciones con valores de cadena de texto o enteros. Para este tipo de comprobaciones se proporciona un comando específico: el comando test.

De nuevo, importante, entiende que la condición del if es la ejecución de un comando!

Para ver las opciones que tiene test vamos a echar un vistazo a su página de manual.

El comando test me sirve para realizar varias comprobaciones, si la comprobación resulta correcta, devuelve un código de retorno de éxito o 0.

Vamos a ver algunas de las comprobaciones que podemos realizar con test a través de la EXPRESION que le pasemos por parámetro.

Cadenas de texto

  • Si una cadena de texto está vacía: -z cadena
  • Si una cadena de texto no está vacía:
    • -n cadena
    • ! -z cadena
  • Si una cadena de texto es igual a otra:
    • cadena1 = cadena2
  • Si una cadena de texto es diferente a otra:
    • cadena1 != cadena2

Números enteros

  • Si un número es menor, menor o igual, igual, diferente, mayor o igual o mayor
    • num1 -lt num2
    • num1 -le num2
    • num1 -eq num2
    • num1 -ne num2
    • num1 -ge num2
    • num1 -gt num2

Comprobaciones con ficheros y directorios

  • Si el fichero existe sea lo que sea
    • -e rutaFichero
  • Si el fichero existe y es un fichero
    • -f rutaFichero
  • Si el fichero existe y es un directorio
    • -d rutaFichero
  • Si el fichero existe y es un fichero especial de bloque
    • -b rutaFichero
  • Si el fichero existe y es un fichero especial de caracter
    • -c rutaFichero
  • Si tenemos permiso de lectura sobre un fichero
    • -r rutaFichero
  • Si tenemos permiso de escritura sobre un fichero
    • -w rutaFichero
  • Si tenemos permiso de ejecución / acceso sobre un fichero / directorio
    • -x rutaFichero

Vamos a jugar

El comando test nos indica que la primera cadena no es igual a la segunda, porque retorna un código de retorno distinto de 0

Seguimos… vamos a sustituir el comando test por los corchetes. OJO: Si utilizamos los corchetes tenemos que tener claro que en realidad estamos ejecutando el comando test, solo que por simplicidad a la hora de crear un script podemos utilizar estos corchetes.

OJO2.0: Recuerda siempre separar con un espacio la expresión de los corchetes.

    Èjercicios

    Pista: Para terminar la ejecución de un script con un código de retorno, utilizamos el comando exit pasándole por parámetro el valor que queramos que retorne.

    Crea un script llamado if06.sh que reciba por parámetro el nombre de un usuario. Si no se pasa parámetro mostrará un mensaje de error y terminará la ejecución del script con un código de retorno 1. exit 1

    Crea un script llamado if07.sh que reciba por parámetro el nombre de un usuario. Si no se pasa parámetro mostrará un mensaje de error y terminará la ejecución del script con un código de retorno 1. Si el usuario no existe en el sistema, mostrará un mensaje de error adecuado y terminará la ejecución del script con un código de error. Si el usuario existe, mostrará la línea de información del usuario en el fichero /etc/passwd.

    Crea un script llamado if08.sh que reciba por parámetro la ruta de un fichero. Si no se pasa parámetro mostrará un mensaje de error adecuado y terminará la ejecución del script con un código de retorno de error. Si el fichero cuya ruta se ha proporcionado no es un fichero o no existe, mostrará un mensaje de error adecuado y terminará la ejecución del script con un código de retorno de error. En caso contrario, mostrará información extendida de permisos, propietario y grupo del fichero (ls -l)

    Crea un script llamado if09.sh que reciba por parámetro la ruta de un directorio. Si no se pasa parámetro mostrará un mensaje de error adecuado y terminará la ejecución del script con un código de retorno de error. Si el directorio cuya ruta se ha proporcionado no es un directorio o no existe, lo creará. A continuación comprobará si el usuario tiene permiso de lectura sobre el directorio o no, mostrando un mensaje para cada caso. Hará lo mismo con el permiso de escritura y de acceso (recuerda, el permio x en directorios es permiso de acceso no de ejecución).

    Crea un script llamado if10.sh que reciba por parámetro el nombre de un directorio y un número. Si no se pasa alguno de los parámetros, se indicará con un mensaje adecuado y se terminará la ejecución del script. Si el nombre del directorio coincide con el nombre del usuario, se mostrará un mensaje advirtiendo dicha situación. Si no existe un directorio con el nombre del directorio en el directorio actual, entonces se creará dicho directorio y se configurará para que tan solo el usuario pueda acceder para realizar operaciones de lectura y escritura. A continuación, si el número proporcionado es menor o igual a 10 se mostrará la información extendida de permisos, propietario y grupo del directorio, no de su contenido. Si el valor es mayor que 10, se creará un fichero llamado leeme.txt dentro del directorio cuyo contenido será la fecha y hora actual.

    Función de error

    Es posible que ya conozcáis las funciones, porque se han trabajado en otros módulos o cursos. Si no es el caso, podéis echar un vistazo al apartado de funciones en la página de Bash Shell Script en www.educatica.es.

    Resumiendo, podemos entender una función en un script de bash como un pequeño script dentro de otro script. Podemos llamar a una función tan solo escribiendo el nombre de la función, pasarle parámetros como lo haríamos con un script y leerlos dentro de la función igual que lo hacemos en el script.

    Para poder utilizar una función, esta debe de haberse creado antes de que la podamos llamar, así pues las funciones las escribimos siempre en la parte superior de nuestro script.

    Lo primero que tenemos que aprender es cómo crear una función. Para ello utilizamos la palabra reservada function seguida por el nombre de la función dos paréntesis y abrimos y cerramos llaves. El código de la función, los comandos que la componen estará comprendido entre las llaves.

    Vamos a crear una función error.

    Se denomina cuerpo de la función a los comandos que están entre las llaves. Esos comandos se ejecutarán, igual que si fuera un script interno, de forma secuencial al llamar a la función en el código del script.

    Si incluimos llamadas a la función error, esta se ejecutará cada vez que la llamemos. La ejecución consiste, igual que con un script, en ejecutar de forma secuencial cada uno de los comandos que la componen.

    Como se puede observar, se ejecutan los comandos del cuerpo de la función cada vez que la invocamos o llamamos dentro del script.

    Como todo script, una función puede recibir parámetros y leerlos. Para pasarle parámetros a una función tan solo tenemos que escribirlos después del nombre de la función separando los parámetros con espacios.

    Para leerlos utilizamos las mismas variables especiales de parámetro que utilizamos con nuestro script.

    Aquí es importante tener presente el ámbito de las variables. Si utilizamos $1 dentro de una función esta variable hace referencia al primer parámetro que se pasa a la función, no al script. Bash busca las variables de parámetro de lo más interno hacia fuera, por tanto $1 dentro de una función hace referencia al primer parámetro de la función. Fuera del script hará referencia al primer parámetro del script.

    En nuestro caso, vamos a utilizar todos los parámetros que se pasan a la función puesto que nos interesan para mostrar un mensaje personalizado.

    Si nos fijamos, en la primera llamada o invocación a la función error no se pasa ningún parámetro, de ahí que no se muestre nada.

    Vamos a enredar con el ámbito de las variables. Mira el siguiente código:

    Qué pasará?

    Cómo podéis ver, dependiendo de dónde se acceda a la variable $@ se mostrarán los parámetros del script o de la función.

    Último paso! terminar la ejecución con código de error. Vamos a hacer que nuestra función error termine la ejecución del script con código de salida.

    Ahora veamos que pasa

    Como podemos observar, la primera vez que se ejecuta la función error se termina la ejecución del script porque se ejecuta el comando exit. De esta forma, no se ejecutarán los comandos de la línea 13 y posteriores.

    Modifica los scripts creados en el apartado anterior para mostrar mensajes de error y terminar la ejecución del script utilizando la función error que acabamos de aprender.