En ocasiones como parte de nuestro trabajo de administración de sistemas, puede que sea conveniente escribir un shell script interactivo, que solicite algún dato al usuario. Pero si el usuario no responde al cabo de cierto tiempo, queremos que el script continúe su ejecución asumiento un valor por defecto para la respuesta.
En este artículo se exploran las posibilidades que existen a la hora de implementar una funcionalidad de este tipo en un script bash
1. Solicitar un dato al usuario y leer la respuesta
El comando read se encarga de leer una línea introducida por el usuario y asignársela a una variable. Casi siempre se utiliza con el modificador «-p» (prompt), para presentar en pantalla un texto en el que se solicita el dato. Por ejemplo. con un script de la forma:
1 2 3 4 5 |
#!/bin/bash read -p "Nombre del fichero: " nombre_fichero echo "El fichero a procesar es: $nombre_fichero" |
obtenemos la siguiente salida:
1 2 3 4 5 |
$ ./lectura_de_teclado.sh Nombre del fichero: datos.txt El fichero a procesar es: datos.txt |
2. Leer más de una variable con un mismo comando read
El comando read admite especificar más de una variable a ser leída. En este caso, el script divide la información introducida por el usuario utilizando como separador el carácter IFS (que por defecto es un espacio en blanco).
Ejemplo:
El siguiente script:
1 2 3 4 5 6 |
#!/usr/bin/bash read -p "Introduzca varios números: " num1 num2 echo "El primer número es $num1" echo "El segundo número es $num2" |
Genera la siguiente salida:
1 2 3 4 5 6 7 8 9 10 11 |
$ ./lectura_de_teclado.sh Introduzca varios números: uno dos El primer número es uno El segundo número es dos $ ./lectura_de_teclado.sh Introduzca varios números: uno dos tres cuatro El primer número es uno El segundo número es dos tres cuatro |
Como vemos, en el segundo caso se han introducido más datos de los que espera el script, y el resultado es que a la última variable especificada ($num2) se le asignan todos los datos restantes.
3. Lectura con timeout
Para quedar a la espera de que el usuario introduzca el dato que se le solicita durante un tiempo limitado, el comando read dispone del modificador «-t», que permite especificar el número máximo de segundos antes de que el script continue su ejecución.
Si se alcanza el tiempo límite, el valor de las variables a leer queda en blanco, y el comando read devuelve un código de status distinto de cero:
1 2 3 4 5 6 7 8 |
#!/bin/bash if read -t 60 -p "Nombre del fichero: " nombre_fichero; then echo "El nombre del fichero es: $nombre_fichero" else echo "Se ha alcanzado el límite de tiempo. Código de salida: $?" fi |
4. Lectura de contraseñas
Si el script necesita solicitar una contraseña al usuario, es interesante hacerlo de tal modo que lo que teclea el usuario no se presente en pantalla. El modificador «-s» del comando read implementa esta funcionalidad:
1 2 3 4 5 6 |
#!/bin/bash read -s -p "Clave de acceso : " mi_clave echo echo "La clave que has introducido es: - $mi_clave" |
5. Lectura del teclado carácter a carácter
Por defecto, el comando «read» espera a que el usuario introduzca una línea completa, finalizando con la tecla «Intro»
Pero en ocasiones, nos puede interesar que el comando lea cualquier tecla que haya sido pulsada, sin esperar a que finalice la línea.
Esto se consigue utilizando el modificador «-n», para especificar el número de caracteres que se desea leer. En particular, con «-n 1» indicamos al comando «read» que finalize en cuanto el usuario haya pulsado una tecla:
1 2 3 4 5 6 |
#!/bin/bash read -n 1 -p "Pulsa una tecla : " mi_caracter echo echo "La tecla pulsada es: - $mi_caracter" |
6. Uso del IFS
El IFS (Input Field Separator) es una variable global cuyo valor es el carácter o caracteres que funcionan como delimitador para separar un valor de otro en la línea leída.
Podemos imprimir el valor del IFS con el siguiente comando:
1 2 3 4 5 |
$ cat -et <<<"$IFS" ^I$ $ |
Como podemos ver, el IFS por defecto es cualquiera de:
- un espacio en blanco
- un tabulador
- un salto de línea
Pero en ocasiones, podemos tener como valor de una variable una secuencia de valores unidos por otro tipo de separador (una coma, dos puntos, etc…)
Cambiando temporalmente el valor del IFS, podemos utilizar el comando «read» para descomponer este valor en los valores individuales de que consta.
Por ejemplo, supongamos que tenemos que procesar una línea en el formato del fichero /etc/passwd
1 2 3 |
linea="miusuario:x:1002:1002::/home/miusuario:/bin/sh" |
Comenzamos por guardar el valor de la variable IFS, para poder recuperarlo más tarde, y asignar temporalmente el carácter «:» como separador
1 2 3 4 |
IFS_anterior="$IFS" IFS=: |
Con esto, podemos descomponer el valor de la línea en los campos de que consta, con el comando «read»:
1 2 3 |
read usuario clave uid gid info home shell <<< "$linea" |
Y por último, volvemos a asignar a IFS el valor que tenía previamente:
IFS=»$IFS_anterior»
—
Muy útiles. ¡Gracias!