lunes, 6 de agosto de 2018

Programación en BASH orientada a los juegos conversacionales.


                                                         (P) Hugo Napoli, 2018                                                        

En este artículo, intentaremos direccionar a la programación hacia los juegos conversacionales, utilizando "Bourne Again SHell" para dicho cometido.
Se describirán conceptos preliminares a tener en cuenta (tales como el funcionamiento de las variables), y -sin perder de vista la orientación mencionada (los juegos conversacionales)-, se desarrollarán procedimientos que siempre conviene saber, explicando además el uso de las siguientes funciones:
  • echo
  • clear
  • sleep
  • read (en varios modos)
  • for-do-done
  • if-then-elif-else-fi
  • bash
  • exit
  • break
  • let
  • declare
  • tput
El artículo fue escrito pensando en los estudiantes de Ciclo Básico de Liceo Areteia, pero bien puede aplicarse a otros estudiantes de otros centros educativos y de otros grados (adultos, por ejemplo), o a toda aquella persona autodidacta que desee aprender programación.






AVISO IMPORTANTE:


Programar en BASH, exige respetar reglas muy específicas. Un solo espacio de más, la ausencia de un signo o símbolo, o una comilla colocada en el lugar equivocado, puede provocar que todo el programa creado falle. Hay que prestar especial atención a ello para obtener resultados exitosos.





1. CONCEPTOS PRELIMINARES.


I) BASH, significa “Bourne Again SHell”.
Es un programa llamado terminal o consola (normalmente también es conocido como “ventana negra” o “pantalla negra”) que le permite al usuario comunicarse directamente con el sistema operativo, de 2 maneras:
  • en tiempo real (introduciendo comandos en la terminal o consola y viendo su resultado inmediato)
  • ejecutando “scripts” (programas escritos en archivos de texto plano y transformados en archivos ejecutables de una manera muy sencilla).
Al haber heredado las características de su sistema padre (Unix), es también compatible con MAC.
Otros sistemas de muy diferente naturaleza, tales como como Windows 10, permiten la instalación (abreviada) de este programa, por lo cual, en teoría, correspondería pensar en que todos los scripts compatibles con estos 3 tipos de sistemas operativos, podrán ser ejecutados en cualquiera de ellos... Siempre y cuando puedas instalar en Windows 10 esta funcionalidad de Linux, lo cual, en muchos casos es imposible.

Si piensas utilizar BASH en una máquina con Windows, te recomiendo que construyas un pen drive con alguna distribución "Linux Live", y así lo utilices de manera nativa (incorporada en cualquier distribución Linux), en lugar de la consola BASH que Microsoft intentó incluir en Windows 10 sin demasiado éxito. Microsoft sigue teniendo suficientes problemas con los que lidiar desde la creación de dicho sistema, como para pretender que una característica importada -y más aún, de esta naturaleza- funcione medianamente bien.


Vista de la terminal nativa del escritorio XFCE (negra, arriba), y de XTERM de X Windows System (blanca, abajo). El sistema que las contiene a ambas es Mageia, en este caso. Son 2 diferentes interfaces para una misma herramienta.


II) Tiempo de ejecución, hace referencia a cuando el programa se está ejecutando, no cuando lo estamos abriendo para modificarlo o trabajar en él.



III) Los comandos, se escriben siempre en minúsculas.



IV) Una variable, es un elemento en donde se guarda cierta información, tal como:
  • nombres de personas para un programa tipo “agenda”,
  • la fecha del día para una aplicación que calcule lapsos de tiempo en días, meses o años,
  • la cantidad de dinero disponible para ser utilizada por un programa de gestión de la economía).
Las variables almacenan datos dentro de sí mismas, permitiendo utilizar esta información o contenido la cantidad de veces que sea necesario, durante la ejecución de un programa.
El contenido de una variable es manejado por un programa (no por un usuario); es limitado y no se guarda en ningún disco duro ni dispositivo de almacenamiento, sino en la memoria RAM; por lo tanto, en la enorme mayoría de los casos, solamente estará disponible mientras se esté ejecutando el programa.
Lo que sea que haya en una variable, puede “variar” (de allí su nombre) o cambiar: nunca ese contenido estará “obligado” a permanecer estático.
Por ejemplo, digamos que estamos realizando un programa para controlar el dinero que hay en la caja de un comercio.
Cuando abre el comercio, el dinero que hay en caja es el de la noche anterior, siempre y cuando no se haya realizado un retiro de efectivo.
Supongamos que estamos hablando de mil pesos. Inventaremos a la variable “caja” y dentro de ella guardaremos esta información.

caja = 1000

A lo largo del día, se venden varios artículos, por un valor total de 6500 pesos, por lo tanto, la variable “caja”, ya no debería contener el valor 1000, sino el valor 7500. Podemos escribir “caja = 7500” para reflejar esta realidad, pero en programación esto generalmente no se realiza de este modo, sino que, en lugar de sobreescribir el contenido anterior, se le suma lo necesario para llegar a que la realidad (el dinero que realmente hay en la caja) coincida con el contenido de la variable del programa informático. Entonces, observa lo siguiente:

caja = caja + 6500

Esto significa que a “caja”, ahora se le suma lo que ya había (1000), más 6500.
“caja”, pasará a contener el valor 7500 en lugar del valor 1000.
También pueden restársele valores:

caja = caja - 2500

Ahora “caja” contendrá el valor 5000… ¿Se va comprendiendo?

Lo mismo puedes hacer con palabras, fechas, etc. El contenido de las variables no se limita estrictamente a números o cifras. El manejo de texto a través de variables, por ejemplo, es algo más complejo, pero es igual de preciso que para valores numéricos.




V) Comando, sentencia, instrucción, orden (en programación), son sinónimos -palabras que hacen referencia a lo mismo-.

VI) Al trabajar con igualaciones o igualdades dentro de un if... then... fi, al tratarse de números, usaremos 1 solo signo de igual (=), pero si estamos trabajando con texto, deberemos utilizar el signo de igual 2 veces (==). Lee la sección VI) if... then... else... fi   (y elif) para obtener ejemplos.



2. CREACIÓN DEL ARCHIVO EJECUTABLE O ARCHIVO DE TRABAJO.


1. Abrir cualquier editor de texto plano (kwrite, kate, gedit, etc).
2. Agregar lo siguiente al inicio del archivo: #!/bin/bash
3. Guardar el archivo como CUALQUIER_NOMBRE.sh
4. Cerrar el archivo.
5. Hacer clic derecho sobre el archivo, seleccionar la opción "Propiedades".
6. En la pestaña "Permisos", marcar la opción "Es ejecutable", o “Permitir que este archivo se ejecute como programa” (u opciones similares), y pulsar en "Aceptar".




3. EJECUCIÓN DEL PROGRAMA.


a. Asegurarse de que el paso 5 de la sección “CREACIÓN DEL ARCHIVO EJECUTABLE O ARCHIVO DE TRABAJO”, haya sido realizado correctamente.
b. Abrir la terminal o consola, arrastrar al archivo hacia dentro de ella, y al soltar el botón izquierdo del ratón, seleccionar la opción "Pegar posición" (si no aparece esta opción, no te preocupes).
c. Hacer clic en la consola y pulsar Enter o Intro.




4. COMANDOS A TENER EN CUENTA.


I) echo

Se emplea para enviar mensajes al usuario (o mostrar texto), y se utiliza así:

echo "Hola, ¿cómo estás?"

El texto se escribe entre comillas. Las comillas no se mostrarán en tiempo de ejecución.
Si quieres mostrar las comillas, debes ANTEPONER los caracteres \"" en tu texto, y finalizarlo con "\"
La sentencia echo quedaría así:

echo \""Hola, ¿cómo estás?"\"

De este modo, no se mostraría 
Hola, ¿cómo estás?
sino
"Hola, ¿cómo estás?"

PRÓXIMAMENTE:

EJEMPLO DE echo $variable
EJEMPLO DE echo -n "texto" PARA EVITAR NUEVA LÍNEA
EJEMPLO DE echo "\t texto1 \texto2 " PARA TABULAR VALORES


II) clear

Borra la pantalla, “limpiando” todo el texto que exista en ella.

Se aplica así:

clear

III) sleep

Aplica una demora (en segundos) antes de continuar con la orden o comando siguiente. Esto es especialmente útil si se pretende respetar los “tiempos humanos”, enlenteciendo los “tiempos de la máquina”. Hay veces en las que la computadora procesa demasiado rápido lo que se le pide, y es necesario aplicar una demora (delay)… Entonces, la podemos mandar a “dormir” (sleep) 1 o 2 segundos, para que la persona no se sienta abrumada por la velocidad con la cual la información le llega.

Se le debe indicar al comando sleep la demora en segundos, y eso se hace de este modo (ejemplo para demorar 1 segundo):

sleep 1

IV) read

Este comando es de gran utilidad cuando necesitamos recoger una orden o datos por parte del usuario durante la ejecución de un programa. Típicamente, el comando “read”, espera la introducción de texto por parte de la persona que está utilizando el ordenador en ese momento.
Cuando un programa muestra un mensaje al usuario tal como “¿Quiere imprimir una copia de este documento?”, o “¿Desea generar un informe?”, evidentemente, habrá que hacer que el ordenador espere y lea la respuesta del usuario, y eso se realiza mediante la instrucción “read”.
Este comando guarda la respuesta en una variable. Aquí podemos ver ejemplos de su sintaxis:

read respuesta
read w
read pepe

En las variables “respuesta”, “w” o “pepe”, será almacenada la respuesta del usuario.
Veamos el siguiente ejemplo.

echo "Por favor, escribe tu nombre."
read nom
echo "Hola, " $nom

En la primera línea de código, se le muestra al usuario un mensaje, pidiéndole que escriba su nombre.
En la segunda línea, se almacena la respuesta en la variable llamada “nom” lo que sea que el usuario haya escrito.
En la última línea, se utiliza el contenido almacenado en la variable “nom”, para saludar a la persona.
Si el usuario introduce “Juan”, el programa devolverá Hola, Juan, pero si el usuario introduce “mañana te lo digo”, el programa mostrará “Hola, mañana te lo digo”.
El signo de “pesos” ($) en la tercera línea, se debe a que las variables se utilizan sin este signo cuando se aplican con “read”, y con este signo en el resto de los casos. Por eso es que la misma variable aparece como “nom” cuando está al lado de “read”, y como “$nom” cuando está actuando conjuntamente con “echo”.
Con el comando “read” (y en la variable “resp”), se almacenará -en un lugar específico de la memoria RAM- la respuesta del usuario al pulsar ENTER, para que podamos utilizar esta información cuantas veces queramos.

Hay otras maneras de utilizar el comando "read", y una de ellas es sin servirnos de la sentencia "echo" para mostrar un mensaje previo al usuario, que le indique lo que debe hacer.
Observemos la siguiente secuencia de códigos citada más arriba:

echo "Por favor, escribe tu nombre."
read nom
echo "Hola, " $nom

Esto puede ser simplificado sustituyendo el primer "echo" y obteniendo el mismo resultado:

read -p "Por favor, escribe tu nombre: " nom
echo "Hola, " $nom

El argumento "-p" (resaltado en verde) que acompaña a la instrucción o comando "read", hace la magia: muestra un mensaje al usuario, y al mismo tiempo, guarda en la variable "nom" lo que haya sido escrito.

También es posible detectar la pulsación de una sola tecla, por ejemplo, para controlar el movimiento de una nave espacial en la pantalla (w, a, s, y d para moverla y la barra espaciadora para disparar), o situaciones por el estilo.
En este caso, ya no se utilizaría read, ni read -p, sino read -n 1 -s, seguido de una variable.
El ejemplo quedaría así:

read -n 1 -s tecla

Entonces, en la variable "tecla", quedaría guardada la primera tecla que el usuario hubiera pulsado.
Si quisiéramos leer únicamente las 2 primeras letras de lo que sea que el usuario ingrese, cambiaríamos el 1 por un 2:

read -n 2 -s tecla

y así, sucesivamente.

V) for... do... done

Este no es un comando, sino un conjunto de varios de ellos, los cuales, entre sí, trabajan para lograr un mismo cometido.
Se utiliza para repetir un determinado procedimiento hasta que ocurra una condición o disparador que lo detenga. Esto es conocido como "bucle" en programación.
Uno de los ejemplos más típicos para esto, sería la situación de tener que esperar una respuesta precisa y excluyente por parte del usuario en un determinado momento en la ejecución de un programa. Por ejemplo, la pregunta “¿Desea salir del programa?” necesita una de dos respuestas (sí o no).
Si responde “no sé”, “puede ser”, u ocurrencias por el estilo, habría que repetir la misma pregunta hasta que el usuario conteste únicamente “sí” o “no”. 
El funcionamiento de este ensamblado de órdenes puede resultar bastante complejo de captar en un inicio, pero en programación, lo que ayuda mucho a terminar de comprender ciertos conceptos, es el uso frecuente de las instrucciones (es decir, la práctica). No hay que desanimarse.

Observemos una pequeña parte del código de un programa voluminoso:

for (( ; ; ))
do
echo "¿Quieres empezar de nuevo? (Sí/No)"
read resp
if [[ $resp == "Sí" ]] || [[ $resp == "sí" ]] || [[ $resp == "Si" ]] || [[ $resp == "si" ]];then
bash "/home/estudiante/Descargas/Juegos conversacionales/Aventura en bash.sh"
exit
else
echo "Que tengas un gran día :)"
exit
fi
done

Analicemos un poco esto.

for (( ; ; ))

Los espacios en blanco, están destinados a que en ellos se coloquen ciertos datos (cuál es el valor inicial de una variable, cuál es el valor máximo que la misma puede llegar a alcanzar, y qué tipo de operación habrá de realizarse con ella [normalmente incremento o decremento]). Como en este ejemplo no han sido necesarios estos valores, es que han quedado en blanco.

"Do", significa "hacer" y "done", significa "hecho".
Si tomamos estas palabras literalmente, no se comprendería del todo el concepto que las mismas encierran.
Simplifiquemos el código, y tomemos el "do" como un "hacer desde aquí", y el "done" como un "hasta aquí", y leámoslo una vez más:

for (( ; ; ))
hacer desde aquí (do)
...INSTRUCCIONES VARIAS...
hasta aquí (done)

¿Queda más clara la idea?

do y done, delimitan un determinado conjunto de acciones que el programador agrupó por algún motivo, no por azar.

Entonces el for, en este caso, se encargará de repetir indefinidamente lo que haya entre el do y el done.

VI) if... then... else... fi   (y elif)

Han sido numeradas las líneas, para hacer más fácil el análisis.
Observemos la otra parte del código, es decir, "lo de adentro" (o "lo que está entre el do y el done"), e identifiquemos las órdenes que van desde el "if" hasta el "fi":

 1. for (( ; ; ))
 2. do
 3. echo "¿Quieres empezar de nuevo? (Sí/No)"
 4. read resp
 5. if [[ $resp == "Sí" ]] || [[ $resp == "sí" ]] || [[ $resp == "Si" ]] || [[ $resp == "si" ]];then
 6. bash "/home/estudiante/Descargas/Juegos conversacionales/Aventura en bash.sh"
 7. exit
 8. else
 9. echo "Que tengas un gran día :)"
10. exit
11. fi
12. done

La utilidad de las líneas 1, 2 y 12 ya han sido comentadas en el apartado "for... do... done".
El comando "echo", en las líneas 3 y 9, posee su sección correspondiente, más arriba.
"read" (línea 4), también se trabajó en sus formas "read" y "read -p", con y sin variables.
"bash" y "exit" (líneas 6, 7 y 10), se verán luego.
Concentrémonos, entonces, en lo que está resaltado con texto en rojo y fondo color amarillo: "if", "then", "else" y "fi".

De manera similar a for, do y done, son comandos que trabajan juntos. En este caso, no forman un bucle, sino una estructura condicional.
Esto significa que la disposición de estos 3 comandos, hace que sus líneas internas analicen una determinada situación, y actúen de una manera u otra, según lo que suceda.

Traduzcamos la línea 5 a "código humano".

Línea 5:

5. if [[ $resp == "Sí" ]] || [[ $resp == "sí" ]] || [[ $resp == "Si" ]] || [[ $resp == "si" ]];then

Línea 5 "traducida":

5. si resp = Sí o resp = sí o resp = Si o resp = si; entonces...

Aquí se está analizando si el contenido de la variable "resp" coincide con alguna de estas 4 posibilidades: Sí, sí, Si o si. ¿Por qué?
Se supone que una persona, para responder afirmativamente, puede escribir la palabra "sí" de varios modos... ya sabemos que algunas de las posibilidades propuestas contienen faltas de ortografía, pero, acaso, cuando leemos con faltas de ortografía, ¿no comprendemos, de todos modos, el mensaje que nos han querido transmitir? ¿Por qué una máquina no puede tener la misma destreza?
En programación, todo lo que escribamos será tenido en cuenta de manera literal y exacta.Para un ser humano, "A" y "a" son las mismas letras; para un ordenador, no.Lo mismo sucede con "1" y "I", e y é, etc.Las 4 maneras más comunes de escribir la palabra "sí", han sido tenidas en cuenta, mas no todas figuran allí. Observa que una persona podría escribir la misma respuesta incluso de otros modos:
sÍ, sI, SÍ, SI
Técnicamente, un "sí", es una respuesta de 2 letras, pero una de ellas lleva tilde. Solo por eso, ya tendríamos 2 maneras de escribirla (sí y si), siempre que no utilicemos las mayúsculas. Al entrar en juego las mayúsculas, ya se amplía el espectro a 8 posibilidades, porque la mayúscula puede estar en primer lugar (Sí), en el segundo lugar (sÍ), en ambos lugares (SÍ), y lo mismo para interpretar ese sí con faltas ortográficas.

Programar no es tan difícil... Lo verdaderamente difícil es adelantarse a la cantidad de cosas que podrían sucederle al usuario y preverlas, para que un simple error no termine haciendo que se cierre un programa, o que la falta de previsión del programador, obligue al usuario a actuar de manera antinatural (sucede con la mayoría de los programas).

Recordemos: lo que importa más es el usuario, no el programa.

El por qué del uso de los paréntesis rectos o corchetes.

Cada posibilidad se escribe encerrada entre un juego de paréntesis de este tipo; por eso es que hay 4 pares de ellos:

5. if [[ $resp == "Sí" ]] || [[ $resp == "sí" ]] || [[ $resp == "Si" ]] || [[ $resp == "si" ]];then

Como se ve, se debe dejar 1 espacio entre los corchetes y lo que sea que haya dentro de ellos.

Sobre la utilización de paréntesis simples o dobles...

Los paréntesis pueden ser utilizados en juegos de 1 y 1 (entrecomillando a las variables) [ "$resp" == "Sí" ] o en juegos de 2 y 2 (tratando a las variables como tales) [[ $resp == "Sí" ]] .
Debido a que no es nada intuitivo encerrar a una variable entre comillas, es que preferimos el uso del paréntesis doble.

Otra razón importante de utilizar doble juego de paréntesis, es que dentro de las variables podría haber palabras con espacios entre medio (como los nombres que habitualmente se dicen juntos: "Juan Martín", "Ana Laura", etc.), o frases ("creo que sí", "esto es correcto", etc.), y esto podría generar errores en tiempo de ejecución si estamos utilizando paréntesis simples (o en juegos de 1 y 1) en lugar de paréntesis dobles.

Continuando con la explicación de "if... then... else... fi", diremos que, luego de la instrucción then, existe algo de código. Y, dicho código, va hasta la instrucción else.
Esto, significa que si no se cumplen las 4 condiciones evaluadas

resp = o resp = o resp = Si o resp = si

entonces (then) deberá hacerse algo

bash "/home/estudiante/Descargas/Juegos conversacionales/Aventura en bash.sh"

exit

de otro modo (else) hacer esto

echo "Que tengas un gran día :)"

exit

fi es utilizado para marcar el final de esta estructura condicional. todo ocurre desde el "if" hasta el "fi". Luego del fi, el programa continúa con su propósito, es decir, "se sale del if".

Si se quisieran colocar más posibilidades, puede utilizarse "elif", y esto se haría del siguiente modo:

if cosas_a_evaluar; then
acciones_a_tomar
elif más_cosas_a_evaluar; then
más_acciones_a_tomar
else
cosas_a_hacer_si_no_se_han_cumplido_las_condiciones_anteriores
fi

"bash" y "exit", se verán a continuación.

VII) bash

Se utiliza para ejecutar un archivo o script. Será muy útil cuando "queramos empezar de nuevo desde el principio", o cuando queramos hacer funcionar un archivo ejecutable desde dentro de otro, o bien desde la consola.

El único parámetro que bash nos pedirá para esto, es el "camino" o la "ruta" hacia el script en cuestión.

Si nuestro script se halla en el escritorio del sistema, y el usuario con el que hemos iniciado sesión se llamara areteia, esta ruta sería la siguiente:

/home/areteia/Mi script.sh

Entonces, haremos lo siguiente. Colocaremos todo eso entre comillas, y se lo pasaremos al comando bash, de este modo:

bash "/home/areteia/Mi script.sh"

También se puede ejecutar el mismo archivo que ya estamos ejecutando. Esto, en un juego, da la sensación de "empezar todo de nuevo".
En el caso anterior

bash "/home/estudiante/Descargas/Juegos conversacionales/Aventura en bash.sh"

es justamente lo que se busca.

VIII) exit

Se utiliza para finalizar un programa, o para salir de él.
Este recurso, se utiliza para que el programa detenga su funcionamiento en uno o en varios puntos del mismo, para que no siga funcionando cuando en realidad ya ha cumplido su función.

Su sintaxis es simple. Solo hay que escribir exit, y bastará para lograr el cometido.
Si escribes exit directamente en la consola, la misma se cerrará de manera automática.

IX) break

Se utiliza cuando se quiere salir de un bucle, no del script.

Dentro de un bucle "for... do... done", puede resultar ideal para "romper" (detener) su funcionamiento y continuar con el programa.

Observemos las siguientes líneas de código:

contador=0
for (( ; ; ))
do
    sleep 0.3
(( contador=contador+1 ))
echo $c
if (( contador==10 )); then
    break
fi
done
echo "Se ha alcanzado el número 10"

Al principio, la variable contador vale cero.
El for, convierte el inicio de esta estructura en un bucle.
Con sleep, se aplica una demora de 1/3 de segundo.
Luego, a contador se le suma 1.
Se muestra el contenido de la variable (que ya no es 0, sino 1).
Y antes de volver al principio (por acción del "do... done"), se verifica que "contador" no haya llegado a 10. De ser así, con break se sale del bucle, pero no del programa, puesto que el mensaje "Se ha alcanzado el número 10" será mostrado, por estar colocado a continuación de "done".
Si sustituyéramos a break por exit, el resultado sería que en lugar de salirnos del bucle "do... done", saldríamos del programa, no mostrándose, de este modo, el mensaje último.

Si se deseara establecer otro tipo de comparación matemática, tal como si contador es mayor, menor, mayor o igual, menor o igual que un número determinado, habría que cambiar la línea
if (( contador==10 )); then
por
if (( contador>10 )); then
y evidentemente el mensaje
echo "Se ha alcanzado el número 10"
por
echo "Se ha sobrepasado el número 10"
ya que estamos comprobando si contador es mayor (>) que 10

Aquí están los comparadores que se pueden utilizar:
  • == igual
  • > mayor
  • < menor
  • >= mayor o igual
  • <= menor o igual
X) let

Técnicamente, el comando "let", no sería imprescindible, puesto que las operaciones que permite realizar, también podemos llevarlas a cabo de otra manera.

let, sirve para asignar un valor a una variable, pero evaluando el posible resultado de esta acción.
Si utilizamos let de este modo

let linux=120

simplemente la variable linux tomará el valor 120.
También podemos utilizar let para asignar el resultado de una operación a una variable, por ejemplo:

let gnu=linux+80

Si linux valía 120 y gnu es linux más 80, el resultado de gnu debería ser 200, ¿cierto?

Ahora bien, si en algún momento alguna variable queda en cero, y se intenta dividir entre cero utilizando let, obtendremos un error. Y esto, sucede sin utilizar let también, lo cual indica que antes de realizar una división, en programación, deberemos analizar los pasos previos con anterioridad.

Observemos:


XI) declare

Esta sentencia, se utiliza para definir variables antes de comenzar a programar, lo cual es una manera muy ordenada y organizada de hacer las cosas.
Cada vez que una variable va a ser utilizada, se la "declara" "arriba del todo", en lugar de ir haciéndolo a lo largo de todo el programa.
En la sección de código siguiente, es posible ver un ejemplo de esto.

#!/bin/bash

clear

read -p "Introduzca el primer número a sumar: " numa
read -p "Introduzca el segundo número a sumar: " numb


let total=numa+numb

echo "El resultado de la suma es de: " $total

Utilizando los parámetros o argumentos correspondientes, podríamos utilizar "declare" para organizar mejor el uso de las variables.

#!/bin/bash

declare -i numa
declare -i numb
declare -i total

clear

read -p "Introduzca el primer número a sumar: " numa
read -p "Introduzca el segundo número a sumar: " numb
let total=numa+numb

echo "El resultado de la suma es de: " $total

Veamos ahora qué parámetros serán sumamente útiles a la hora de trabajar con "declare" y veremos de este modo por qué este comando hace la diferencia.

declare -i pepe
convierte a pepe en una variable para números enteros (integers), los cuales son casi todos, exceptuando a los decimales o a las fracciones.
Lo que sucederá cuando se introduzca texto en una variable declarada con el argumento "-i", será que se generará el valor cero en esa oportunidad.

Por ejemplo, siendo
numa=10
y
numb="quique",
al sumarlas, el resultado de numa+numb, será 10+quique, es decir, 10+0, es decir: 10

declare -l pepe
convierte a pepe en una variable para texto en letras minúsculas (lower case letters).
Esto es muy útil para los casos mencionados en el apartado VI) if... then... else... fi   (y elif), resaltado en verde (*).

declare -u pepe
convierte a pepe en una variable para texto en letras mayúsculas (uppercase letters).
Esto también es muy útil para los casos mencionados en el apartado VI) if... then... else... fi   (y elif), resaltado en verde (*).

Sin argumentos, declare creará una variable preparada para conter cualquier tipo de dato, sin importar si es texto, números enteros, decimales, etc.
Por lo tanto, en este caso, sería lo mismo utilizar o no "declare", quedando a criterio del programador realizar lo siguiente:

declare texto
texto="Esta es una variable para letras y números"

o bien, directamente

texto="Esta es una variable para letras y números"

(*) Cuando hay que analizar el contenido de una variable que ha sido "llenada" por un usuario, es mejor "pasar todo a minúsculas" antes de "ver qué introdujo el usuario en ella".
Con el siguiente ejemplo, se comprenderá la complejidad que a veces surge al analizar respuestas en apariencia simples.  

read -p "¿Deseas dirigirte al Este? Responde únicamente sí o no, por favor: " resp
if [[ $resp == "sí" ]] || [[ $resp == "sÍ" ]] || [[ $resp == "Sí" ]] || [[ $resp == "SÍ" ]];then
echo "¡Allá vamos!"
elif [[ $resp == "no" ]] || [[ $resp == "nO" ]] || [[ $resp == "No" ]] || [[ $resp == "NO" ]];then
echo "Entonces, iremos al Oeste"
fi

Todo esto se simplificaría (e incluso quedaría más ordenado) utilizando declare con el argumento -l para la variable resp:

declare -l resp


read -p "¿Deseas dirigirte al Este? Responde únicamente sí o no, por favor: " resp
if [[ $resp == "sí" ]];then
echo "¡Allá vamos!"
elif [[ $resp == "no" ]];then
echo "Entonces, iremos al Oeste"
fi

¿No luce más sencillo?

XII) tput

El comando tput puede ser un gran recurso a la hora de "maquillar" nuestra pantalla.
Con él, se puede escribir en negrita, cambiar de color el fondo y las letras, y utilizado en combinación con otros parámetros e incluso comandos, veremos que resultará de gran utilidad.

Utilizaremos esta modalidad del comando tput para cambiar el color de las letras

tput setaf NÚMERO

y esta para cambiar el color del fondo

tput setab NÚMERO

siendo:
  • setaf (significado de "set active foreground") el color para el texto,
  • setab (significado de "set active background") el color para el fondo,
  • y NÚMERO, un número entre el 0 y el 255 (ambos incluidos).
Aquí dejo 2 muestras de lo que tput puede hacer junto con el parámetro setaf:



También se puede escribir "en negrita", mediante el parámetro "bold", simplemente así:

tput bold

Para "dejar todo como estaba" (color de letra y de fondo predefinidos y volver al "texto plano"), ejecutaremos:

tput sgr0

Te dejo un par de scripts creados por mí, especialmente para que experimentes con estas posibilidades.


2a versión del script, mejorada para propósitos estéticos:


El script está comentado, y muestra mucho más que estos 16 colores básicos.

Antes de ejecutarlo, recuerda los puntos 5 y 6 de la sección de este mismo post "2. CREACIÓN DEL ARCHIVO EJECUTABLE O ARCHIVO DE TRABAJO.". De otro modo, el script no funcionará.

Hallarás más información relevante aquí https://linux.101hacks.com/ps1-examples/prompt-color-using-tput/ (muy básico e intuitivo, aunque esté en inglés).



5. PROCEDIMIENTOS MUY ÚTILES.

a) Encontrar una palabra dentro de una oración.

Pensemos en qué utilidad puede tener esto.
Una de las tantas, es el análisis de una respuesta dada por el usuario, en la búsqueda de una palabra clave.
Si a una persona se le hacen preguntas que solo debe responder utilizando monosílabos como "sí", "no", "ir", "sur", o palabras de significado estricto como "este", "oeste", "norte", "abrir", "cerrar", etc, el juego se haría algo aburrido y "robotizado", ¿no es así?
Pues bien: encontrar la palabra "sí" dentro de una respuesta más extensa, permitiría dar la sensación de estar frente a un programa cargado de "inteligencia artificial", lo cual, se traduciría en la impresión o noción de que "la computadora me entiende".

Veamos cómo funciona esto.

Es más que común que un programa realice una pregunta al usuario para saber cómo proceder.
Ejemplo:
Se hace tarde y aún sigues dando vueltas en el bosque, sin tu mapa del lugar tras haber extraviado tu mochila de viaje. Tampoco posees señal para que tu gps funcione.Anochecerá pronto, y este hecho, sumado al frío que comienza a sentirse, enciende dentro de ti la luz amarilla de la precaución.¿Seguirás las indicaciones que has visto en los viejos carteles de madera que indican que existe una cabaña, posiblemente abandonada, 300 metros hacia el norte?
Aquí la situación parece ser simple: si el jugador escribe "sí", pasará al siguiente nivel, y se hallará dentro de una vieja y deteriorada cabaña, en donde, al menos, podrá pasar la noche sin mayores sobresaltos.
Si escribe "no", deberemos pensar en una situación diferente: noche, frío, humedad, aullidos de lobos, y el descubrimiento de una caverna para guarecerse.
Los juegos de aventuras, siempre son así :)

Pero, ¿qué tal si la persona responde lo siguiente?
Sí, iré a la cabaña para estar a salvo.
o algo como
No. No le tengo miedo a un bosque nocturno. Es solo naturaleza en estado normal.
Si el programa está esperando un simple "sí" o un "no", estas respuestas generarán errores y confusión, ya que se generarán mensajes desde el ordenador como "No he comprendido tu respuesta. Inténtalo de nuevo." mientras el usuario piensa "Vaya... qué máquina más tonta... le estoy diciendo que no y no lo entiende". Lo mismo ocurriría con un "sí".

Veamos, entonces, cómo encontrar un "sí", un "no", o cualquier palabra dentro de una frase, oración o enunciado, observando estas líneas de código dentro de un script.

read -p "¿Seguirás las indicaciones que has visto en los viejos carteles de madera que indican que existe una cabaña, posiblemente abandonada, 300 metros hacia el norte?" resp

if [[ $resp == *"sí"* ]]; then
     echo "Has llegado a la cabaña. Aquí podrás pasar la noche sin sobresaltos"
elif [[ $resp == *"no"* ]]; then
     echo "La noche fría y húmeda, el aullar de los lobos y la sensación de soledad te invaden. Afortunadamente, a lo lejos divisas una caverna."
else
     echo "Inténtalo otra vez. No he comprendido lo que has escrito"
fi

Como vemos, tanto el sí como el no, están enmarcados por asteriscos... ¿Por qué?

Un asterisco a la izquierda de un sí (*sí), significa: "no importa lo que haya antes del sí, mientras haya un sí".
Un asterisco a la derecha de un sí (sí*), significa: "no importa lo que haya después del sí, mientras haya un sí".
Entonces, un asterisco a la izquierda de un sí y otro a su derecha (*sí*), significan: "no importa lo que haya antes ni después del sí, mientras haya un sí".

Ahora, las respuestas como "Sí, iré a la cabaña para estar a salvo." o "No. No le tengo miedo a un bosque nocturno. Es solo naturaleza en estado normal.", serán comprendidas con exactitud por la computadora.

Es mejor que esperar monosílabos... ¿no crees?

b) Leer líneas de un archivo de texto.

Es necesario este recurso cuando se pretende "levantar" información para que un programa funcione mediante archivos de configuración, es decir, cuando al programa hay que "cargarle" información al inicio, antes que el usuario interactúe con él.
Poniendo un ejemplo de un videojuego cualquiera, podemos comprobar que al ejecutarlo, el mismo "recuerda" varias cosas sobre nuestra interacción con el mismo, por ejemplo, cantidad de energía, comida, "vidas", dinero restantes, etc. Si se trata de un juego de pelea u guerra, qué armas, armaduras, protecciones y potenciadores adicionales poseemos, por ejemplo.
Eso se logra guardando toda esa importante información antes de que el usuario salga del juego, para que al ingresar a él, la próxima vez, el escenario en donde alguna vez estuvo el usuario, se mantenga sin cambios, lo cual es conocido como la acción de "poder continuar desde el último punto guardado".

Esto se logra mediante este conjunto de comandos, escritos de esta manera:

#!/bin/bash

clear
archivo="/home/estudiante/Escritorio/prueba.txt"
while ifs= read -r texto
do
  echo $texto
done < $archivo

Antes que nada, hay que aclarar que para que esto funcione, debe existir en el Escritorio (en este caso) un archivo con información dentro, llamado prueba.txt
De otro modo, el ejemplo no funcionará.
Evidentemente, en la enorme mayoría de los casos, el usuario no será "estudiante", sino otro, por ejemplo: Estela. En ese caso, hay que cambiar la línea
archivo="/home/estudiante/Escritorio/prueba.txt"
por
archivo="/home/estela/Escritorio/prueba.txt"

El parámetro "-r" le indica a read que lea línea a línea, en lugar de leer todo el texto de una sola vez.
Un archivo de configuración no debe ser leído todo de una sola vez, sino línea por línea y asignando el contenido de cada lectura a una variable diferente.

ifs es un parámetro que se le pasa al comando while (en este caso). Es una especie de variable especial, es decir, una variable que es utilizada por bash, y se utiliza exactamente como en el ejemplo anterior, más allá de que pueda poseer otros modos de utilización.


Enlace a un excelente artículo sobre ejemplos de comandos en BASH (en inglés):




Fuentes consultadas:

https://unix.stackexchange.com/questions/47584/in-a-bash-script-using-the-conditional-or-in-an-if-statement

https://stackoverflow.com/questions/3427872/whats-the-difference-between-and-in-bash

http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_09_05.html

https://askubuntu.com/questions/385528/how-to-increment-a-variable-in-bash

http://tldp.org/LDP/abs/html/comparison-ops.html

https://unix.stackexchange.com/questions/134437/press-space-to-continue

https://askubuntu.com/questions/939294/difference-between-let-expr-and

http://wiki.bash-hackers.org/commands/builtin/let

https://stackoverflow.com/questions/32896549/division-by-zero-using-shell

https://ryanstutorials.net/bash-scripting-tutorial/bash-arithmetic.php

https://bash.cyberciti.biz/guide/Create_an_integer_variable

https://www.gnu.org/software/bash/manual/bash.html#Bash-Builtins

https://stackoverflow.com/questions/4277665/how-do-i-compare-two-string-variables-in-an-if-statement-in-bash

https://www.cyberciti.biz/faq/unix-howto-read-line-by-line-from-file/

https://www.linuxjournal.com/content/bash-arrays

https://linuxhint.com/compare_strings_bash/

https://linux.101hacks.com/ps1-examples/prompt-color-using-tput/

https://stackoverflow.com/questions/525872/echo-tab-characters-in-bash-script

https://www.tecmint.com/echo-command-in-linux/

http://www.linuxask.com/questions/how-to-echo-a-tab-in-bash

https://stackoverflow.com/questions/11193466/echo-without-newline-in-a-shell-script

https://www.cyberciti.biz/faq/bash-for-loop/

https://stackoverflow.com/questions/4181703/how-to-concatenate-string-variables-in-bash

http://tldp.org/LDP/abs/html/comparison-ops.html

ENTRADA EN CONSTRUCCIÓN

No hay comentarios.:

Publicar un comentario