(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.
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.
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
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:
Esto puede ser simplificado sustituyendo el primer "echo" y obteniendo el mismo resultado:
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
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.
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":
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 = Sí o resp = sí o resp = Si o resp = si
entonces (then) deberá hacerse algo
exit
de otro modo (else) hacer esto
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
"bash" y "exit", se verán a continuación.
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.
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.
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
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"
Aquí están los comparadores que se pueden utilizar:
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:
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
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
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
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
echo "Entonces, iremos al Oeste"
fi
¿No luce más sencillo?
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
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:
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).
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 = Sí o resp = sí 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_tomarelif más_cosas_a_evaluar; then
más_acciones_a_tomarelse
cosas_a_hacer_si_no_se_han_cumplido_las_condiciones_anterioresfi
"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 )); thendone
break
fi
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
#!/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
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" ]];thenif [[ $resp == "sí" ]] || [[ $resp == "sÍ" ]] || [[ $resp == "Sí" ]] || [[ $resp == "SÍ" ]];then
echo "¡Allá vamos!"
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" ]];thenif [[ $resp == "sí" ]];then
echo "¡Allá vamos!"
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).
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.
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.
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?
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"
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.
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.
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