Ya hemos visto que operar con decimales en Bash no es imposible, aunque es farragoso. Ahora veremos que pasa lo mismo para comparar decimales. Pero lo explico y dejo una función para que quien tenga que comparar, la copie en su script.
Primero comprobaremos si le hemos pasado bien los parámetros $1 y $2 a la función. Comprobamos si existen y tienen contenido. Si esto ocurre, comprobaremos que realmente son números que opcionalmente tienen un punto.
En caso de que sólo hubiera una variable numérica, se devuelve esa, ya que esa será la mayor.
Si las variables no fueran numéricas, se devuelve -1 de error.
Si ambas variables son nuḿéricas, se dividen en la parte entera y la parte decimal. Es decir, se extrae la subcadena desde el inicio hasta el punto, que es la parte entera, y la subcadena desde el punto hasta el fina, que es la parte decimal.
Comparamos las partes enteras. Si las partes enteras son distintas, el número mayor es el que tenga la parte entera mayor.
En caso de que las partes enteras sean iguales, seguimos haciendo comprobaciones:
Como recortamos subcadenas, igualamos su longitud con ceros al final de la subcadena más corta. Para que 0.02 no de mayor que 0.1.
Una vez que sabemos que la función ha recibido dos variables, que estas son numéricas con o sin punto, que hayamos separado la parte entera y la decimal, que las partes enteras son iguales y que las partes decimales tienen los mismos caracteres, comparamos los decimales.
Y devolvemos el parámetro cuya parte decimal sea mayor.
En caso de que la parte entera sea igual y la parte decimal sea igual tamibén, es que los dos números son iguales. Así que nos da igual cuál devolver. Devolveremos $1.
Y aquí el script con comentarios porque la función es un poco liosa:
<
[code]#!/bin/bash
function cualesmayor()
{
# Función que recibe dos parámetros con números decimales o no
# y devuelve el mayor de los dos
# Comprobamos que recibimos los dos parámetros,
# si recibimos solo uno, devolvemos ese
if [ "$1" ] && [ "$2" ]
then
# Existen ambas variables, por lo que podemos compararlas
# Comprobamos si ambos parámetros están formados únicamente
# por números y un único .
caracteresvalidos='^[0-9]+([.][0-9]+)?$'
if [[ $1 =~ $caracteresvalidos ]] && [[ $2 =~ $caracteresvalidos ]]
then
# Ambos son numéricos. Por lo que separaremos
# en la parte entera y la parte decimal
# Dónde está el punto. Si no hubiera punto, la posición es 0
let posicionpunto1=$(expr index $1 ".")
let posicionpunto2=$(expr index $2 ".")
# Extraemos las partes enteras.
if [ $posicionpunto1 -lt 0 ]
then
let parteentera1=$1
else
let numerodigitosparteentera1=$posicionpunto1-1
let parteentera1=${1:0:$numerodigitosparteentera1}
partedecimal1=${1:$posicionpunto1}
let numerodigitosparteentera2=$posicionpunto2-1
let parteentera2=${2:0:$numerodigitosparteentera2}
partedecimal2=${2:$posicionpunto2}
# Comprobamos las partes enteras
if [ $parteentera1 -gt $parteentera2 ]
then
echo $1
elif [ $parteentera1 -lt $parteentera2 ]
then
echo $2
else
# Ambas partes enteras son iguales
# Comprobamos las partes decimales
# Primero igualamos con ceros al final
# la longitud de las cadenas
tamdecimales1=${#partedecimal1}
tamdecimales2=${#partedecimal2}
if [ $tamdecimales1 -gt $tamdecimales2 ]
then
let diferencia=$tamdecimales1-$tamdecimales2
for i in $(seq 1 $diferencia)
do
partedecimal2=$partedecimal2"0"
done
elif [ $tamdecimales1 -lt $tamdecimales2 ]
then
let diferencia=$tamdecimales2-$tamdecimales1
for i in $(seq 1 $diferencia)
do
partedecimal1=$partedecimal1"0"
done
fi
if [ $partedecimal1 -gt $partedecimal2 ]
then
echo $1
elif [ $partedecimal1 -lt $partedecimal2 ]
then
echo $partedecimal2
else
# Ambas partes enteras son iguales
# Ambas partes decimales son iguales
# Ambos números son iguales
# Devolvemos el primero
echo $1
fi
fi
fi
else
echo -1
fi
else
if [ "$1" ] && [ ! "$2" ]
then
# $1 existe y no está vacío y $2 no existe o está vacío.
# Lo contrario nunca ocurrirá, ya que sin $1 no puede haber $2
echo $1
else
# Si llegamos aquí es porque no existe $1
echo -1
fi
fi
}
let operador1=3
let operador2=2
let operador3=5
let operador4=1
division1=$(echo "scale=4; $operador1/$operador2" | bc)
division2=$(echo "scale=4; $operador3/$operador4" | bc)
mayor=$(cualesmayor $division1 $division2)
echo "El primer resultado es "$division1" y el segundo resultado, "$division2". El mayor es "$mayor[code]
Para saber si una variable existe y está inicializada con un valor distinto a vacío, es decir, que no se ha inicializado con:
variable=""
Lo podemos comprobar con [ "$variable" ]
Veamos un ejemplo y vamos a ir evolucionándolo para ver sus posibilidades:
#!/bin/bash
if [ "$variable" ]
then
echo "La variable existe y no está vacía"
else
echo "La variable no existe o está vacía"
fi
Devuelve:
La variable no existe o está vacía
Ahora vamos a probar con la variable inicializada vacía:
#!/bin/bash
variable=""
if [ "$variable" ]
then
echo "La variable existe y no está vacía"
else
echo "La variable no existe o está vacía"
fi
Y nos vuelve a dar:
La variable no existe o está vacía
Así que vamos a dar un valor. En un alarde de imaginación, a la variable variable le voy a dar un valor de valor:
#!/bin/bash
variable="valor"
if [ "$variable" ]
then
echo "La variable existe y no está vacía"
else
echo "La variable no existe o está vacía"
fi
Y al ejecutarlo, devuelve:
La variable existe y no está vacía
Para comprobar que no está vacía, lo que podemos hacer es comprobar si existe. Si existe y no contiene nada es que su valor es "". Esto lo haremos con [ -n "${variable-unset}" ], que dará verdadero si no existe:
#!/bin/bash
variable="valor"
if [ "$variable" ]
then
echo "La variable existe y no está vacía"
else
if [ -n "${variable-unset}" ]
then
echo "La variable no existe"
else
echo "La variable existe pero está vacía"
fi
fi
Que nos devuelve:
La variable existe y no está vacía
Y ahora probaremos si la cadena está vacía:
#!/bin/bash
variable=""
if [ "$variable" ]
then
echo "La variable existe y no está vacía"
else
if [ -n "${variable-unset}" ]
then
echo "La variable no existe"
else
echo "La variable existe pero está vacía"
fi
fi
Que devuelve:
La variable existe pero está vacía
Y sin variable (la comento):
#!/bin/bash
# variable=""
if [ "$variable" ]
then
echo "La variable existe y no está vacía"
else
if [ -n "${variable-unset}" ]
then
echo "La variable no existe"
else
echo "La variable existe pero está vacía"
fi
fi
Que devuelve:
La variable no existe
Para hacer operaciones sencillas en Bash podemos usar let, pero únicamente operaciones muy sencillas. Veamos un ejemplo:
#!/bin/bash
let operador1=3
let operador2=2
let suma=$operador1+$operador2
let resta=$operador1-$operador2
let multiplicacion=$operador1*$operador2
let division=$operador1/$operador2
let modulo=$operador1%$operador2
echo "La suma de "$operador1" + "$operador2" es "$suma
echo "La diferencia de "$operador1" + "$operador2" es "$resta
echo "El producto de "$operador1" x "$operador2" es "$multiplicacion
echo "El cociente de "$operador1" / "$operador2" es "$division
echo "El resto de "$operador1" / "$operador2" es "$modulo
Al ejecutarlo nos da este resultado:
La suma de 3 + 2 es 5
La diferencia de 3 + 2 es 1
El producto de 3 + 2 es 6
El cociente de 3 / 2 es 1
El resto de 3 / 2 es 1
Ufffff... ¡Pi es 3 exactamente!
No, no, que no te de un infarto, que pi no es 3. Y en Bash podemos calcularlo sin estos sobresaltos. Aunque no lo haremos con let, sino con bc.
De hecho, podemos indicar cuántos decimales queremos a través de scale. Veamos una aproximación a pi:
echo "scale=2; 355/113" | bc
3.14
Y con cuatro decimales:
echo "scale=4; 355/113" | bc
3.1415
Y con seis decimales:
echo "scale=6; 355/113" | bc
3.141592
Como se puede comprobar, esta división se aproxima a pi, pero el binomio bc/scale no redondean, únicamente cortan la muestra de decimales. Pero al menos muestran decimales, que let no lo hace.
Visto esto, modificamos el script del ejemplo:
#!/bin/bash
let operador1=3
let operador2=2
let suma=$operador1+$operador2
let resta=$operador1-$operador2
let multiplicacion=$operador1*$operador2
division=$(echo "scale=2;
$operador1
/$operador
2" | bc)let modulo=$operador1%$operador2
echo "La suma de "$operador1" + "$operador2" es "$suma
echo "La diferencia de "$operador1" + "$operador2" es "$resta
echo "El producto de "$operador1" x "$operador2" es "$multiplicacion
echo "El cociente de "$operador1" / "$operador2" es "$division
echo "El resto de "$operador1" / "$operador2" es "$modulo
Nótese que he quitado el let a la hora de definir la variable $division. Al ser decimal no lo acepta let.
El resultado que nos devuelve el script:
La suma de 3 + 2 es 5
La diferencia de 3 + 2 es 1
El producto de 3 x 2 es 6
El cociente de 3 / 2 es 1.50
El resto de 3 / 2 es 1
Esto ya es más bonito. Si decimos que pi es 3 exactamente que sea para llamar la atención, como el Profesor Frink, no porque nuestro script no sepa calcular con decimales.
Cuando usando una aplicación nos autocompleta el texto, nos subraya una palabra o nos dice que algo está incorrectamente escrito, no es magia, es que esa aplicación hace uso de un diccionario.
Aunque hay aplicaciones que tienen diccionarios propios, el sistema también tiene al menos uno genérico del que podemos hacer uso o podemos usarlo como base para añadirle nuevas palabras.
En Ubuntu, este diccionario está en:
/usr/share/dict/spanish
Pero no necesariamente todas las distribuciones tienen por qué guardar el diccionario de español en esa dirección, por lo que podemos buscar en nuestro sistema con:
find /usr -iname spanish | grep dict
Y nos dirá la dirección en el arbol de directorios del sistema de nuestro diccionario.
Ya sabemos cómo seleccionar aleatoriamente una tipografía para incluir texto en ImageMagick, pero nos puede ocurrir que, al ser aleatoria esa selección, nos elija una tipografía que sean dibujos, signos matemáticos o cualquier otra tipografía que no sea de letras, también nos puede mostrar una tipografía con un alfabeto distinto al nuestro o que no muestre bien algunos caracteres.
Yo siempre me fijo en cuatro caracteres clave: apertura de interrogación y exclamación, vocales con tilde y eñes.
Así pues, voy a usar una cadena de caracteres que tenga interrogaciones, una vocal con tilde y una eñe:
¿Leña? ¡Porrón!
Y como quiero ver qué tipografías que tengo instaladas en cada máquina muestran correctamente estos caracteres, hago los siguientes pasos:
#!/bin/bash
directoriotemporaltipografias="tipografias"
if [ ! -d directoriotemporaltipografias ]
then
mkdir $directoriotemporaltipografias
fi
for tipografia in $(convert -list font | grep "Font" | cut -d " " -f 4)
do
convert -pointsize 100 -font $tipografia label:"¿Leña? ¡Porrón!" $directoriotemporaltipografias/$tipografia.jpg
done
Veamos qué es lo que hace:
Indico que es un script en bash:
#!/bin/bash
Que el directorio con el que voy a trabajar se llama "tipografias", pero lo meto en una variable por si por cualquier motivo quiero cambiar de directorio:
directoriotemporaltipografias="tipografias"
Compruebo si existe o no ese directorio y si no existe, lo creo:if [ ! -d directoriotemporaltipografias ]
then
mkdir $directoriotemporaltipografias
fi
Hago un bucle en el que recorre todas las tipografías tal como vimos en el artículo anterior sobre tipografías en ImageMagick: y le digo que me genere una imagen por cada tipografía con la cadena antes indicada de "¿Leña? ¡Porrón!" guardando esas imágenes con el nombre de la tipografía utilizada en el directorio creado para tal efecto:for tipografia in $(convert -list font | grep "Font" | cut -d " " -f 4)
do
convert -pointsize 100 -font $tipografia label:"¿Leña? ¡Porrón!" $directoriotemporaltipografias/$tipografia.jpg
done
Con esto, tengo un directorio lleno de imágenes con cadenas de texto. Abro el directorio con un navegador de ficheros que muestre una previsualización de cada imagen y selecciono todas las imágenes que no me interesan por el motivo que sea, que generalmente es porque no escribe bien todos los caracteres indicados:
Otras veces es porque, aunque muestre correctamente los caracteres, no me parecen lo suficientemente legibles o son muy específicos de una determinada fecha: navideños, de Hallowen, con nieve...
Una vez borrados los que en una primera ronda en bruto me han parecido que no cubren mis necesidades (ojo, que cuando seleccionamos aleatoriamente una tipografía es bastante arriesgado poner a trabajar ese script y desentendernos, por lo que es mejor que, en caso de duda, borrar el fichero), paso a una segunda fase en la que miro una por una las tipografías.
A veces nos encontramos que una tipografía escribe correctamente los caracteres pero no nos queremos arriesgar a que un script la seleccione sin nuestra aprobación:
Estas tipografías muestran todos los caracteres, pero quizá no encajen en todos los contextos, por lo que las borro también.
Y una vez que he borrado todo lo que sobra, ejecuto un segundo script:
#!/bin/bash
directoriotemporaltipografias="tipografias"
for fichero in $(ls $directoriotemporaltipografias)
do
echo ${fichero%.*} >> tipografiasausar.txt
done
rm -r $directoriotemporaltipografias
rmdir $directoriotemporaltipografias
En el que le indico:
Que es un script bash:
#!/bin/bash
Que recoja los ficheros de un determinado directorio:directoriotemporaltipografias="tipografias"
Que recorra ese directorio fichero a fichero y quite la extensión de esos ficheros ${fichero%.*} e incluya el nombre sin extensión en un fichero de texto llamado tipografiasausar.txt añadiendo en cada pasada de for cada tipografía al texto ya existente en ese fichero (>>) for fichero in $(ls $directoriotemporaltipografias)
do
echo ${fichero%.*} >> tipografiasausar.txt
done
Que borre el contenido de ese directorio y el propio directorio para que no ocupe espacio en disco ni moleste una vez hecha la selección. rm -r $directoriotemporaltipografias
rmdir $directoriotemporaltipografias
Una vez creado el fichero tipografiasausar.txt para seleccionar aleatoriamente una línea de ese fichero lo podemos hacer con shuf. Así:
tipografia=$(shuf -n 1 tipografiasausar.txt)
Y así quedaría un script de ejemplo:
#!/bin/bash
tipografia=$(shuf -n 1 tipografiasausar.txt)
convert -pointsize 100 -fill Blue -font $tipografia label:"Automaticen\ny sean felices" automaticen.png
En ImageMagick tenemos el parámetro -list que lista los valores que pueden recibir algunos parámetros.
Uno de los parámetros del que se puede listar los valores que puede recibir es font, que es el parámetro que permite seleccionar la tipografía con la que escribir un texto con annotate o label, así pues, si escribimos:
convert -list font
convert nos mostará las tipografías de nuestro sistema con una serie de datos más, como la familia, la dirección...
Font: Zoidal-BRK
family: Zoidal BRK
style: Normal
stretch: Normal
weight: 400
glyphs: /usr/share/fonts/truetype/aenigma/zoidal.ttf
Font: Zrnic
family: Zrnic
style: Normal
stretch: Normal
weight: 400
glyphs: /usr/share/fonts/truetype/larabie/zrnic___.ttf
Font: Zurklez-Outline-BRK
family: Zurklez Outline BRK
style: Normal
stretch: Normal
weight: 400
glyphs: /usr/share/fonts/truetype/aenigma/zurklezo.ttf
Font: Zurklez-Solid-BRK
family: Zurklez Solid BRK
style: Normal
stretch: Normal
weight: 400
glyphs: /usr/share/fonts/truetype/aenigma/zurklezs.ttf
Font: Æ-Systematic-TT-BRK
family: Æ Systematic TT BRK
style: Normal
stretch: Normal
weight: 400
glyphs: /usr/share/fonts/truetype/aenigma/aesymatt.ttf
Font: Ænigma-Scrawl-4-BRK
family: Ænigma Scrawl 4 BRK
style: Normal
stretch: Normal
weight: 400
glyphs: /usr/share/fonts/truetype/aenigma/aescrawl.ttf
Sin embargo, pese a que convert muestra toda esa información el nombre de la tipografía que vamos a usar a la hora de generar un texto con ImageMagick es el que sucede a "Font:". Así pues, si lo que queremos es saber el nombre con el que podremos invocar a las tipografías desde convert para añadir textos en nuestras imágenes o generar imágenes con texto, podremos listar únicamente esas líneas. Y aquí es donde llamamos a nuestro amigo grep:
convert -list font | grep Font
Font: Zakenstein-Rotalic
Font: Zekton
Font: Zekton-Bold
Font: Zekton-Bold-Italic
Font: Zekton-Dots
Font: Zekton-Italic
Font: Zelda-DX-TT-BRK
Font: Zenith-BRK
Font: Zephyrean-BRK
Font: Zephyrean-Gust-BRK
Font: Zero-Threes
Font: Zero-Twos
Font: Zero-Velocity-BRK
Font: ZeroHour
Font: Zirconia-BRK
Font: Zirconia-Cubic-BRK
Font: Zodillinstrisstirust
Font: Zoetrope-BRK
Font: Zoidal-BRK
Font: Zrnic
Font: Zurklez-Outline-BRK
Font: Zurklez-Solid-BRK
Font: Æ-Systematic-TT-BRK
Font: Ænigma-Scrawl-4-BRK
Otra fórmula sería extraer la columna donde están las cadenas de texto que contienen los nombres de las tipografías, el comando cut ayuda mucho en esta tarea:
convert -list font | grep Font | cut -d " " -f 4
Con estos parámetros le decimos a cut, que es un comando que "corta" cadenas, que el separador es un espacio en blanco con -d " ".
Del mismo modo, le podríamos indicar que el separador de columnas es cualqueir otro caracter o cadena de caracteres. El parámetro -f lo que indica es el número de la columna que ha de mostar, en este caso, la cuarta. Veamos:
Zekton
Zekton-Bold
Zekton-Bold-Italic
Zekton-Dots
Zekton-Italic
Zelda-DX-TT-BRK
Zenith-BRK Zephyrean-BRK
Zephyrean-Gust-BRK
Zero-Threes Zero-Twos
Zero-Velocity-BRK
ZeroHour Zirconia-BRK
Zirconia-Cubic-BRK
Zodillinstrisstirust
Zoetrope-BRK
Zoidal-BRK
Zrnic
Zurklez-Outline-BRK
Zurklez-Solid-BRK
Æ-Systematic-TT-BRK
Ænigma-Scrawl-4-BRK
Y ahora que ya tenemos el listado limpio, únicamente tenemos que elegir una cadena al azar. Lo podemos hacer con shuf: convert -list font | grep Font | cut -d " " -f 4 | shuf -n 1
Intersect-O-BRK
Y si lo probamos muchas veces, veremos que el resultado es aleatorio, por lo que cada vez que ejecutemos esa instrucción será distinta:
Y dentro de un script, el uso sería igual:
#!/bin/bash
tipografia=$(convert -list font | grep Font | cut -d " " -f 4 | shuf -n 1)
echo "La tipografía seleccionada de forma aleatoria ha salido: "$tipografia
Veamos si así funciona:
Como vemos, cada vez que ejecutamos el script nos muestra una tipografía distinta.
Y ahora, vamos a usarlo con ImageMagick para generar una imagen con una tipografía aleatoria:
#!/bin/bash
tipografia=$(convert -list font | grep Font | cut -d " " -f 4 | shuf -n 1)
convert -pointsize 100 -font $tipografia label:"Disfruten del\nSoftware Libre" disfruten.png
Y vamos a hacer un for para comprobar que realmente genera imágenes con distintas tipografías:
#!/bin/bash
for i in $(seq 1 3)
do
tipografia=$(convert -list font | grep Font | cut -d " " -f 4 | shuf -n 1)
convert -pointsize 100 -font $tipografia label:"Disfruten del\nSoftware Libre" disfruten$i.png
done
Vista la forma básica de generar textos con tipografías aleatorias, veremos también cómo hacer una selección previa de las tipografías para usar aquellas tipografías que se ajusten a nuestras necesidades.