educación, informática y demás

GNU/Linux bash, Shell scripts

Comprobando ficheros: comando test e if..elif..else

En este caso práctico vamos a trabajar con el comando test para comprobar información sobre ficheros. Además, utilizaremos el comando if para realizar distintas acciones en nuestro script en función de las características de un determinado fichero.

Comprobaciones de ficheros

Como humanos desde la shell (humanos)

Recuerda que como administradores, utilizando comandos, podemos realizar distintas comprobaciones en el sistema. Por ejemplo, si un usuario existe o no. También podemos hacer este tipo de comprobaciones con ficheros.

No obstante, ¿cómo podemos comprobar si un fichero o directorio tiene determinadas características desde un script?. Por ejemplo si un fichero existe y además tenemos permiso de lectura sobre él.

Como administradores, podemos comprobar si el fichero existe y si tenemos permiso de lectura sobre el con un comando: ls

Ahora interpretamos la información. El fichero no existe. Como humanos leemos los datos y sabemos que el fichero no existe.

Vamos a crear el fichero 🙂

Ahora veamos si existe y podemos leer.

Ejecutamos el comando ls de nuevo y ahora, como humanos, tenemos que interpretar la información que nos muestra el comando. Según esta información, el fichero existe, y además todo el mundo, propietario, grupo y resto, tienen permiso de lectura. Nuestro usuario, alumno que podemos ver en el prompt, es el propietario y por tanto también tiene permiso de escritura.

Lo que acabamos de hacer es interpretar información.

Veamos que pasa con un directorio.

De nuevo interpretamos la información que nos muestra el comando ls, esta vez con la opción -ld. La d mostrará información del directorio no de su contenido.

Gracias a la d que aparece a la izquierda de la información que muestra ls, sabemos que programacion es un directorio, no un fichero. Sabemos que existe, porque muestra información. Sabemos que todo el mundo tiene permiso de acceso y lectura, y además alumno tiene permiso de escritura.

Repito, toda esta información nos la muestra el comando adecuado y nosotros, como humanos, tenemos que interpretarla.

La pregunta, ahora es ¿cómo usamos esta información dentro de un script? Porque seguro que habrá veces en las que queramos automatizar algún proceso con un script en el que tengamos que realizar este tipo de comprobaciones.

Dentro de un script (programación)

Lo primero que podemos pensar es en utilizar el comando ls dentro de un if, como comando-condición. Esto puede servir para comprobaciones simples, por ejemplo si un fichero existe.

Con esto podemos comprobar si el fichero cuya ruta esté en la variable filePath existe.

Le podemos dar una vueltita para que reciba la ruta por parámetro.

Vamos a probarlo ahora pasando la ruta de un fichero que no existe

Vamos a darle otra vueltita, refinamiento progresivo, vamos a evitar que el comando que estamos utilizando como condición en el if muestre datos por pantalla: redirección de salida y error a /dev/null, porque esos datos no los queremos para nada.

Esto funciona, y es una comprobación simple de ficheros: si existe o no. Pero, ¿qué pasa si tenemos que comprobar que el fichero existe y es un directorio? o, peor todavía, ¿y si tenemos que comprobar que tenemos permiso de escritura sobre el directorio?

Eso con el comando ls dentro de un script, automatizándolo dentro de un script, puede ser complicado.

Si algo es complicado y se utiliza mucho, se le suele dar una solución más sencilla utilizando un comando concreto. En este caso, podemos usar el comando test.

Resulta que el comando test permite comparar cadenas de texto, valores enteros y comprobar ficheros.

Vamos a ver que opciones tenemos para comprobar tipos de ficheros y vamos a jugar con ellas.

Con estas opciones podemos comprobar si un fichero existe y de que tipo es.

Por ejemplo, vamos a comprobar que el fichero cuya ruta se proporciona por parámetro que exista y sea un fichero normal.

Comprobamos si el fichero existe y es un fichero regular.
Este código es exactamente igual que el anterior. Al escribir dos corchetes estamos indicando que se ejecutará el comando test con la expresión (parámetros) que están entre los corchetes

La condición del test solo se cumplirá si el fichero existe y es un fichero regular. En cualquier otro caso, no se cumplirá.

El directorio programacion existe, pero es un directorio, no un fichero.

Ahora vamos a cambiar nuestro código para comprobar si es un directorio en lugar de un fichero.

Ahora vamos a comprobar simplemente que el fichero exista, nos da igual el tipo de fichero que sea.

Ahora… queremos comprobar si el fichero existe y es un fichero regular, si es así mostramos un mensaje advirtiendolo. Sino si existe y es un directorio mostramos un mensaje diciendo que es un directorio. Sino si el fichero existe, aunque sea de otro tipo, mostramos un mensaje diciendo que existe pero no es ni un fichero regular ni un directorio. Sino, si no existe, mostramos un mensaje indicando que el fichero no existe.

Vamos a analizar lo que nos dicen.

Si el fichero existe y es un fichero regular, si es así mostramos un mensaje advirtiendolo

Seguimos… Sino si existe y es un directorio mostramos un mensaje diciendo que es un directorio.

Sino si el fichero existe, aunque sea de otro tipo, mostramos un mensaje diciendo que existe pero no es ni un fichero regular ni un directorio.

Sino, si no existe, mostramos un mensaje indicando que el fichero no existe.

Si llegamos al else en la línea 12 significa que no se ha cumplido ninguna de las condiciones anteriores, es decir que el fichero no existe.

Comprobando permisos de acceso

Ahora vamos a comprobar si tenemos permiso de lectura y/o escritura sobre el fichero en cuestión. De nuevo, consultamos la ayuda del comando test para comprobar que opción tenemos que seleccionar.

Indicamos si tenemos permiso de lectura o no. Indicamos si tenemos permiso de escritura o no. Indicamos si tenemos permiso de acceso, en caso de ser un directorio o de ejecución en caso de ser un fichero. Ojo! vamos a tener que diferenciar entre uno u otro, fichero o directorio, para dar un mensaje adaptado.

Indicamos si tenemos permiso de lectura o no. Indicamos si tenemos permiso de escritura o no.

Comprobación del permiso de lectura
Comprobamos el permiso de lectura y escritura

Ahora tenemos que comprobar si tenemos permiso de acceso / ejecución. Como el permisos depende del tipo de fichero, tenemos que comprobar además el tipo de fichero que es: si es un directorio sería acceso sino sería ejecución.

De esta forma, primero comprobamos si tenemos permiso de acceso/ejecucción en el fichero.

Si tenemos permiso de ejecución, entonces comprobamos si es un directorio o no para mostrar un mensaje, de permiso de acceso o de permiso de ejecución.

Comprobamos el tipo de fichero para mostrar un mensaje concreto si es un directorio y otro si no lo es.

Sino, es decir si no tenemos permiso de acceso/ejecución, hacemos lo mismo: comprobamos si es un directorio o no, para mostrar el mensaje adecuado.

Comprobamos de nuevo el tipo de fichero, entre directorio u otro tipo, para mostrar un mensaje u otro

Otra opción mejor se´ria comprobar el tipo de fichero antes de comprobar el permiso de acceso/ejecución.

Comprobando propietario y grupo

Vamos a comprobar si somos el propietario del fichero y/o pertenecemos al grupo. Si no somos ni propietario ni pertenecemos al grupo, entonces debemos mostrar un mensaje diciendo que somos «de los otros».

El problema es que comando, que condición, tengo que poner ahí para saber que no soy ni el propietario ni el grupo. No puede cumplirse la condición -O ni la -G

NO eres el propietario Y NO eres del grupo

  • test ! -O $filePath -a ! -G $filePath
  • [ ! -O $filePath -a ! -G $filePath ]

Ejercicios de scripting

Version 01 – Para cada fichero…

Vamos a cambiar el script que hemos elaborado hasta ahora para que recorra los ficheros del directorio actual en lugar de recibir la ruta del fichero por parámetro.

De esta forma, para cada fichero en el directorio actual, se mostrará la información del fichero que hemos elaborado hasta ahora.

Vamos a repetir, para cada fichero, el proceso de comprobaciones que hemos elaborado, recorriendo la lista de ficheros del directorio actual.

Podemos crear primero el bucle que recorrerá cada uno de los ficheros del directorio actual. Comprobamos que funciona y después metemos en el cuerpo del bucle, una vez que sepamos que funciona, las operaciones que tenemos que llevar a cabo para cada uno de los ficheros.

Además vamos a aprovechar la variable filePath que será la que usemos para recorrer la lista de ficheros del directorio actual.

El comando exit de la linea 8 nos sirve para probar el bucle y que no se continúe ejecutando el código que tenemos debajo.

En el ejemplo de ejecución vemos que funciona, aunque le hemos pasado por parámetro un valor. En esta versión del script, no usamos los parámetros, así que ese valor se perderá como lágrimas en la lluvia.

Ahora que sabemos que el bucle funciona como esperamos, vamos a meter el resto del programa dentro del bucle.

Este código funciona, pero es dificil razonar porque tenemos dentro del cuerpo del bucle un montón de comandos. Si nos fijamos podríamos agrupar varios comandos juntos porque tienen una misma funcionalidad o un mismo objetivo o una misma finalidad: mostrar información de un fichero.

¿Y si metemos todos esos comandos en una sola función?

Ahora el código del script quedaría reducido a un bucle que llama a una función llamada showFileInfo. Para cada fichero en el directorio actual, la variable filePath toma su nombre de fichero. A continuación se llama a la función showFileInfo.

A continuación, podemos analizar qué hace la función showFileInfo.

La función showFileInfo muestra información del fichero cuya ruta está almacenada en la variable filePath

Versión 03 – Divide y vencerás

La función mola, pero es muy grande. Podríamos dividirla en más funciones para facilitarnos su comprensión. Esta división la podemos hacer internamente a la función de forma que desde fuera no haya que cambiar nada. Es decir, el bucle continuará igual.

El código quedaría como sigue, para el cuerpo principal del script.

No ha cambiado, sigue llamando o ejecutando la función showFileInfo.

Veamos el contenido de la función showFileInfo

Esta función llama, en ese orden, a tres funciones más: showFileType, showPermission y showUserAccess. Estas funciones están definidas más arriba. Vamos a ver el código de cada una de ellas.

La función showFileType tan solo se centra en mostrar el tipo del fichero cuya ruta está almacenada en la variable global filePath.

A continuación, la funcióm showPermission muestra los permisos de acceso que tiene el usuario actual en el fichero cuya ruta se almacena en la variable filePath.

Por último, la función showUserAccess muestra información de acceso del usuario actual sobre el fichero cuya ruta se almacena en la variable global filePath.

En realidad, lo que hemos hecho es dividir la funcionalidad del script en funciones con un nombre significativo que nos indica qué hace la función. Después hemos llamado a cada función para realice la tarea que tiene realizar, usando la función showFileInfo como función principal que se encarga de llamar a las tres funciones de información.

Podríamos haber prescindido de esta función, showFileInfo, y llamar dentro del cuerpo del bucle a las tres funciones. No obstante, se ha optado por esta opción para mostrar como podemos ir refinando el desarrollo de nuestro programa sin tener que modificar el programa principal, dividiendo una función en varias funciones que son llamadas dentro de esa función: descomposición de funciones. La funcionalidad, el resultado es el mismo, pero es mucho más fácil de comprender.

El código del script completo es el siguiente:

Ejercicios propuestos

A continuación se proponen un par de ejercicios en los que tendremos que modificar nuestro script un poco.

Ejercicio 1 – Ruta del directorio por parámetro. checkDirFiles.sh

El nombre del script será checkDirFiles.sh.

En esta versión, el script debe recibir la ruta del directorio cuyos ficheros se recorrerán por parámetro. Si el usuario no pasa ningún parámetro se deberá mostrar un mensaje de error y terminar con la ejecución del script devolviendo un código de salida de error.

Si el directorio cuya ruta se ha obtenido en el primer parámetro no existe o no es un directorio se deberá mostrar un mensaje de error y terminar la ejecución del script con un código de salida de error.

El script recorrerá el directorio cuya ruta se ha pasado por parámetro y para cada fichero dentro de ese directorio, se mostrará información del fichero, utilizando la función showFileInfo que hemos implementado a lo largo de este caso práctico.

Ejercicio 2 – Ruta del directorio por parámetro. checkMoreFiles.sh

El nombre del script será checkMoreFiles.sh.

En esta versión, el script debe solicitar al usuario la ruta del directorio a recorrer por teclado.

Mientras el usuario inserte la ruta de un directorio, el script comprobará si el directorio existe y mostrará información sobre los ficheros están dentro de dicho directorio. Si el directorio no existe o no es un directorio, se mostrará un mensaje indicando que no es un directorio, pero no se terminará la ejecución del script.

El script recorrerá el directorio cuya ruta se ha escrito por teclado y para cada fichero dentro de ese directorio, se mostrará información del fichero, utilizando la función showFileInfo que hemos implementado a lo largo de este caso práctico. Podemos utilizar el bucle for que hemos utilizado a lo largo del caso práctico, pero teniendo en cuenta que la ruta del directorio a recorrer la habremos obtenido de un comando read.

Por último, debemos cerciorarnos de volver a pedir ruta de directorio para terminar alguna vez con el bucle while.

Dejar una respuesta