Hola amigos, en esta edición les vengo a contar de un par de truquitos que dejan tus bash scripts josha josha. Nótese que es necesario conocimiento básico de bash scripting para entender qué está pasando.
Comencemos...
Funciones
Dentro de un script de bash se pueden poner funciones, que vendrían a ser pedazos de código que reusaremos a lo largo del script.
La nomenclatura es la siguiente:
function nombre_función {
comandos
}
O, si querés hacer a un programador C feliz:
function nombre_función() {
comandos...
}
Con el bonus de que es más portable de ésta manera (léase, otros shells lo reconocerían sin problemas).
Veamos un ejemplo.
#!/bin/bash
function saludar_root () {
echo "Hola, super usuario! You have the powah!"
}
function saludar_user () {
echo "Hola, $USER, bienvenido a bordo"
}
if [ "$USER" = root ]; then
saludar_root
else
saludar_user
fi
Declaramos una función "saludar_root" y otra "saludar_user". Luego, en el main usamos un if para indicar cuándo se usa cada función.
Output:
Hagamos buen uso de esas funciones.
Tput
Coloreá tu output siguiendo la gama de colores default: Verde para todo OK, amarillo para advertencias (el script continúa pero te avisa que tenés un problema) o rojo para errores (el script deja de ejecutarse por tal error).
Acá vemos un scripcito:
#!/bin/bash
# Declaracion variables
NORMAL=$(tput sgr0)
VERDE=$(tput setaf 2; tput bold)
AMARILLO=$(tput setaf 3)
ROJO=$(tput setaf 1)
# Declaracion funciones
function rojo() {
echo -e "$ROJO$*$NORMAL"
}
function verde() {
echo -e "$VERDE$*$NORMAL"
}
function amarillo() {
echo -e "$AMARILLO$*$NORMAL"
}
# Aca empieza el main
# Para imprimir salidas exitosas
verde "La tarea ha finalizado correctamente."
# Para imprimir errores
rojo "La configuración no existe."
# Para imprimir advertencias
amarillo "Debes usar una versión más nueva."
Output:
Esos son scripts con onda!
Como siempre, pueden consultar todo el potencial de tput yendo a su respectivo manual. Igual acá les dejo una pequeña guía:
tput y los colores
tput setab [1-7] – Settea un color de background usando
tput setb [1-7] – Settea un color de background
tput setaf [1-7] – Settea un color de foreground usando escape ANSI
tput setf [1-7] – Settea un color de foreground
tput y formatos de texto
tput bold – Negrita
tput dim – Habilita el modo half-bright (brillo medio)
tput smul – Comienza subrayado
tput rmul – Termina subrayado
tput rev – Enciende modo reverso
tput smso – Entra modo standard (negrita en rxvt - emulador de x en win, ej: cygwin).
tput rmso – Sale de modo standard
tput sgr0 – Apaga todos los atributos
Código de colores:
0 – Negro
1 – Rojo
2 – Verde
3 – Amarillo
4 – Azul
5 – Magenta
6 – Cyan
7 – Blanco
Códigos de salida
Cada script al terminar devuelve un código de salida, 0 si todo fue bien, más de 0 si hubo algún error. Este código se almacena en la variable $?:
Podemos ver en el ejemplo que el echo se ejecutó sin problemas, por eso devolvió 0, y el ls tuvo un problema, porque no encontró el archivo que le pedí, así que devuelve un código de error 2.
Qué usos tiene? Por ejemplo, podemos saber si un comando que queremos ejecutar en nuestro script tuvo problemas o no, así decidimos si continuamos o si hacemos algo distinto para salvar el error.
Un ejemplo: Quiero crear un script que le agregue una línea al archivo lalala.txt, pero sólo si el archivo existe. Si no existe, quiero que el script se deje de ejecutar luego de lanzar un error.
Si hacemos
echo "Este es un script de prueba" >> lalala.txt
El archivo se va a crear, lo cual puede servir para muchos casos, pero no para el que acabo de plantear. Entonces lo que vamos a hacer es hacer un ls de lalala.txt, fijarnos si el código de salida es 0 (el archivo existe) o distinto de 0 (archivo no existe). Si el archivo existe, vamos a agregarle una línea:
#!/bin/bash
FILE=lalala.txt
ls $FILE > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo "Archivo $FILE no existe"
exit 2
fi
echo "Este es un script de prueba" >> $FILE
Output:
Nótese que al lanzar el error, hice que el script devolviera 2 para indicar que no corrió satisfactoriamente y para que el error del ls (ls: cannot access lalala.txt: No such file or directory) no molestara, mandé el standard output (> y el standard error (2> al /dev/null, que vendría a ser un agujero negro del sistema y ama alimentarse de porquería.
Usando argumentos
Veamos el ejemplo anterior del cat en el archivo. Necesitamos que si existe el archivo lalala.txt, escriba una línea, pero si no existe, que salga con un error. El contenido del script está arriba. Pero digamos que ahora necesito hacer lo mismo con blah.txt. Qué hago?
1. Modificar el archivo, reemplazando lalala.txt por blah.txt (abrir archivo en vim, apretar : y escribir %s/lalala.txt/blah.txt/g. (Dos puntos es para decirle al vim que quiero que ejecute algo, % es para que lo haga en la totalidad del archivo y no sólo en la línea en la que estoy parado, s/ es para sustituir y la g es para hacerlo global, léase, todas las ocurrencias de una línea). Guardo cambios y listo.
Pero y si tengo 20 archivos para modificar? voy a estar cambiando el nombre cada vez que necesito ejecutarlo?
2. Pasar el nombre del archivo por argumento.
#!/bin/bash
FILE=$1
ls $FILE > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo "Archivo $FILE no existe"
exit 2
fi
echo "Este es un script de prueba" >> $FILE
Ahí le asignamos a la variable $FILE el valor del primer argumento, $1. Lo mismo pueden hacer con $2, $3, blah blah. Output:
En $0 está almacenado el nombre del script, y en $# está la cantidad de argumentos pasados, en nuestro ejemplo, uno, el nombre del archivo.
Imprimir Debug
Ahhh, el debug, herramienta esencial de todo programador. Acabás de hacer una cuenta monstruosa y querés saber si salió bien? Debug. Asignaste algo a una variable y querés saber si se asignó correctamente? Debug. Querés dar un poco más de info para que el user no piense que el script quedó trabado? Debug.
#!/bin/bash
DEBUG=$1
function debug() {
if [[ $DEBUG -gt 0 ]]
then
echo ">>> $*"
fi
}
debug "Estamos en el directorio $PWD"
debug "Mostraremos el contenido del directorio"
ls
Nótese que al principio declaré la variable DEBUG y le asigné $1, o sea, el primer argumento del script, y en la función debug tenemos un condicional si debug es mayor que 0.
Veamos lo que sale cuando lo corremos normalmente vs cuando lo prendemos (pasandole el valor 1 como argumento):
Nótese que también se pueden usar varios niveles de debug, al pasar como argumento 2 y agregando otro nivel al if.
Va queriendo, eh?
Chequear si existe un ejecutable
Utilizando which y el código de sálida podemos asegurarnos que ese ejecutable que necesitamos está en el sistema. Usando como ejemplo el curl:
OK=0
FAIL=1
function require_curl() {
which curl &>/dev/null
if [ $? -eq 0 ]
then
return $OK
fi
return $FAIL
}
Notese la redirección a /dev/null una vez más.
Para hacerlo más corto, se puede devolver el código de salida y evaluar eso:
function require_curl() { which "curl" &>/dev/null; }
function require_curl() { which -s "curl"; }
Longitud de un string
A veces necesitamos chequear la longitud de un string. Por ejemplo, necesitamos que nos garantizen que un password tenga al menos 8 caracteres. Cómo hacemos ésto?
if [ ${#PASSWD} -lt 8 ]
then
rojo "El password es demasiado corto"
exit 1
fi
${#nombre_de_var} almacena la longitud de un string.
WARNING! Recuerden no escribir passwords en scripts, hagamos campaña por scripts seguros!
Función usage
Esta es la función que explica lo que hace el script. Se usa mayormente para explicar su uso: cuántos y cuáles argumentos u opciones usar.
Se podría hacer con varios echos, peeeero... Miren ésta versión:
cat << EOF
Usage: miscript <comando> <argumentos>
VERSION: 1.0
Comandos Disponibles
install - Instala paquete
uninstall - Desinstala paquete
update - Actualiza paquete
list - Lista paquetes
EOF
El "cat << EOF" indica imprimí todo lo que siga, hasta que encuentre de nuevo la señal EOF (end of file)". Cool, no? Miremos el output en una consola:
La condición que usé:
if [ $# -lt 1 ]; then
usage
exit 3
fi
Usar variables de entorno
Muchas veces necesitamos usar una variable de entorno del usuario. Recuerdan el script que saludaba? Utilizamos la variable de entorno $USER.
law@pulpo ~/test $ env | grep USER
USER=law
Digamos que queremos usar la variable URL, pero el usuario que estoy usando no lo tiene definido. Así que vamos a decirle al script "usá la variable de usuario, pero si no existe, usá tal otra":
URL=${URL:-http://localhost:8080}
Simple y bonito!
Leer desde teclado con timeout
Le pedis al usuario que escriba algo. Pero el usuario cuelga, y cuelga y cuelga... Por qué tenemos que bancarnos a los colgados? Pongamos un timeout de 60 segs:
READ_TIMEOUT=60
read -t "$READ_TIMEOUT" input
Eso es todo para la lección de hoy. Espero que hayan aprendido algo, y que sus scripts queden FA BU LO SOS. Personalmente prefiero perl, así que veré si hago algún otro thread para scripts de perl con onda.