educación, informática y demás

Clases de informática, GNU/Linux bash, Informática, Shell scripts, Sistemas operativos

Primeros bash shell scripts

Queremos que cualquier usuario pueda ejecutarlo.

Para ejecutarlo hay que poner la ruta del fichero a ejecutar. Si solo ponemos el nombre del fichero es posible que bash no lo encuentre, salvo que esté en algún directorio de la lista de directorios almacenados en la variable PATH.

helloWorld.sh no está en el PATH

Es decir, bash busca el binario o ejecutable en los directorios que están almacenados en la variable de entorno PATH en el mismo orden en el que se encuentran. Estos directorios son los directorios donde están los binarios en el sistema.

Como no tenemos el script en ninguno de estos directorios, tenemos que proporcionar la ruta del script, ya sea ruta absoluta o relativa.

Gracias a la primera línea del script, sabe qué comando o programa tiene que utilizar para ejecutar el script.

iniciaDirectorios.sh

Crea un un script llamado iniciaDirectorios.sh que se encargue de crear una serie de directorios en tu directorio personal.

Primero creará los directorios ciclo, proyectos y pruebas en el directorio personal del usuario actual.

Deberás crear un fichero leeme.txt dentro de cada uno de estos directorios con un saludo de bienvenida.

Dentro del directorio ciclo, creará los directorios 2020 y 2021. Dentro de cada uno de estos directorios, 2020 y 2021, se deben crear los siguientes directorios: redes, sistemas, bbdd y develop.

Solución

Un script es un fichero que contiene un conjunto de comandos ordenados que se ejecutarán en secuencia, en el orden en el que aparecen en el fichero. Por tanto, lo que tenemos que hacer en el script es utilizar los comandos que necesitemos ejecutar en el orden adecuado para que realicen las funciones que nos solicitan.

Un script nos permitirá automatizar la realización de una serie de pasos, que se ejecutarán, uno detrás de otro, tan solo ejecutando el script.

De esta forma, lo que vamos a hacer es determinar que comandos tenemos que ejecutar para llevar a cabo cada una de las tareas que nos solicitan.

Pero, primero vamos a crear el esqueleto del script.

Esto lanza gedit en un nuevo proceso, de forma que podremos escribir nuestro script y seguir trabajando con la terminal. como le hemos pasado por parámetro el nombre del fichero, el editor de texto sabe que tenemos intención de grabar el contenido del documento en este fichero concreto: iniciaDirectorios.sh.

Como siempre, lo primero el esqueleto básico del script:

  1. Linea con identificación del interprete de comandos a utilizar.
  2. Descripción de la función
  3. Datos de autor, contacto y fecha

Primero creará los directorios ciclo, proyectos y pruebas en el directorio personal del usuario actual.

mkdir ciclo proyectos pruebas

Deberás crear un fichero leeme.txt dentro de cada uno de estos directorios con un saludo de bienvenida.

echo «Bienvenido al sistema $USER» > leeme.txt

cp leeme.txt ciclo

cp leeme.txt proyectos

mv leeme.txt pruebas

Dentro del directorio ciclo, creará los directorios 2020 y 2021. Dentro de cada uno de estos directorios, 2020 y 2021, se deben crear los siguientes directorios: redes, sistemas, bbdd y develop.

Para crear los directorios 2020 y 2021 podemos hacerlo de dos formas distintas:

  • mkdir ciclo/2020 ciclo/2021
  • mkdir ciclo/{2020,2021}

En el caso de los directorios redes, sistemas, bbdd y develop, lo ideal es usar llaves. De hecho, podríamos haber creado los directorios 2020 y 2021 utilizando la opción -p de mkdir con un solo comando.

mkdir -p ciclo/{2020,2021}/{redes,sistemas,bbdd,develop}

La primera aproximación sería la siguiente

En la línea 14 podríamos haber escrito cada una de las rutas en lugar de usar las llaves. Pero esto llevaría a tener que escribir 8 rutas distintas y además dificultaría la legibilidad y facilidad de modificación del script.

Sin embargo, tiene un pequeño problema. Da por supuesto que el directorio actual es el directorio personal del usuario actual.

Segunda aproximación

El fallo del script está en que presupone que el directorio actual, desde donde se ejecuta el script, coincide con el directorio personal del usuario y esto no tiene porqué ser así.

La solución, utilizar la variable ~ o HOME para acceder a la ruta absoluta del directorio personal del usuario actual.

En este caso vamos a utilizar la variable ~ delante de cada ruta.

iniciaUserDir.sh

Vamos a darle una vueltita al script que hemos creado en el punto anterior. El objetivo es que, como administradores, podamos crear todos esos directorios y ficheros para un usuario concreto sin que cada usuario tenga que ejecutar el script.

Es decir, que al ejecutar el script decidamos para qué usuario vamos a crear los directorios, y el script se encargue de crearlos en el directorio personal de dicho usuario.

Para ello, vamos a usar una variable interna que llamaremos usuario que contendrá el nombre del usuario para el queremos llevar a cabo la incialización.

También supondremos que el directorio personal de dicho usuario se llamará como el usuario y estará dentro del directorio /home. No estaría mal usar otra variable 🙂

Ahora tenemos que utilizar la variable homeDir para hacer referencia al directorio personal del usuario actual.

Este script se debe ejecutar por el administrador. Tiene un problema el script, que ya veremos cuál es.

Lo debemos lanzar con sudo.

El problema son los permisos.

La idea es cambiar, al menos, al propietario para ello usamos, como última línea el cambio de propietario de forma recursiva.

Si queremos ejecutarlo con otro usuario, tan solo tenemos que cambiar el valor de la variable.

Vamos a ejecutarlo de nuevo a ver si funciona.

Aquí lo ideal sería recibir el nombre del usuario por parámetro.

Script iniciaUsuario.sh

Este script partirá del script anterior, es decir es el mismo, pero recibirá por parámetro el nombre del usuario para el que queremos crear los directorios de trabajo.

Para ello, el cambio que tenemos que hacer, a priori, es muy simple. Tan solo tenemos que asignar a la variable usuario el valor recibido en el primer parámetro.

El interprete de comandos nos permite consultar los valores que se pasan al script en el momento de ejecutarse por parámetro gracias a unas variables especiales de solo lectura.

Con esto lo que hemos hecho es asignar el valor recibido en el primer parámetro a la variable usuario. No tenemos que modificar absolutamente nada del código del script y además le damos un nombre significativo al valor que hemos recibido por parámetro. Es más comprensible hacer referencia a la variable usuario, que nos indica que el dato que guarda es un usuario, que a la variable $1 que sabemos que contiene el primer parámetro.

¿Y si el usuario no existe?. El problema es que va a generar una serie de directorios en /home para un usuario que no existe y además nos dará un mensaje de error el comando chown.

Además nos habrá creado al final e.l directorio /home/pepe

El problema es que el usuario no existe. Nosotros no podemos saber si el usuario existe o no existe hasta que no se ejecuta el script y se pasa el nombre del usuario por parámetro.

Así que deberíamos comprobar de alguna forma si el usuario existe en el sistema antes de continuar. Si no existe, no deberíamos continuar, mostraríamos un mensaje de error y terminaríamos la ejecución del script.

Para hacer esto tenemos que utilizar el comando if que nos permite ejecutar comandos si se cumplen una condición.

La forma básica de utilizar el if es: «Si se cumple la condición entonces ejecuta una serie de comandos». La condición se comprueba ejecutando un comando, es decir, si el comando que pongamos en la parte de condición se ejecuta correctamente entonces se cumple la condición y se ejecutarán los comandos que estén en el then. Si no, es decir el comando devuelve un valor distinto de 0 porque ha habido un error, entonces no se cumple la condición y no se ejecutarán los comandois que estén en el then.

Para hacer una negación, es decir que se ejecuten los comandos cuando no se cumpla la condición tenemos que utilizar el caracter !.

El valor de retorno es distinto de 0

Podemos comprobar si el usuario no existe con el comando if.

Está bien, comprueba que el usuario no existe (!) y si no existe (es decir el comando id da un error) entonces muestra un mensaje en pantalla.

El problema es que esta aproximación muestra en pantalla la salida del comando id. Podemos redirigir las salidas estandar y de error del comando a /dev/null.

Si no queremos que nos muestre ningún mensaje.

Y si no pasan el parámetro…

Ahora el problema está en si no nos pasan ningún parámetro. Deberíamos comprobar que $1 no está vacío.

En el momento de escribir el script, nosotros como programadores o administradores, sabemos que sucede en ese momento. Por ejemplo, podemos controlar el valor que tendrá una variable cuyo valor se lo asignamos en el momento de escribir nuestro script. Esto se llama tiempo de compilación, en el momento de crear o escribir el script.

Sin embargo, no podemos saber qué hará el usuario cuando ejecute nuestro script. Esto se llama tiempo de ejecución.

Una de los sucesos que tenemos que controlar es que el usuario nos pase los valores mínimos necesarios por parámetro y que esos valores sean correctos.

En el punto anterior hemos comprobado que el nombre del usuario sea de un usuario válido, sin embargo no controlamos que nos estén pasando un valor por parámetro.

¿Qué pasará si el usuario ejecuta el script sin pasar parámetro?

La varible usuario estará vacío, y la variable homeDir apuntará al directorio /home.

La comprobación con el id funcionará, porque se ejecutará el comando id sin parámetros que nos mostrará información del usuario actual y, por tanto, no dará error.

Después, es probable que nos trata de crear una serie de directorios dentro del directorio /home.

Para evitar este comportamiento erroneo debemos comprobar si se ha pasado parámetro, es decir si $1 no está vacío.

La sentencia que nos permitirá comprobar que se pasa parámetor sería: «Si está vacío $1 entonces mostraremos un mensaje de error y terminaremos con el script con código de error».

Para comprobar el valor de cadenas de texto utilizaremos el comando test.

Como test es un comando que se utiliza muchisimo en los condicionales, se ha implementado de forma simple con dos corchetes. De forma que, podemos escribir una expresión de las recogidas en test entre dos corchetes y será lo mismo que ejectura el comando test.

En nuestro script quedaría así:

myDirectory.sh

Dejar una respuesta