DIY Tasker. Parte 1
DIY Tasker. Hazlo tu mismo
Os comenté en el artículo anterior que en vez de daros directamente el código de TASKer
mi gestor de tareas, lo que vamos a hacer es ver como lo he programado en bash, en una especie de curso-tutorial para que podais contruir el vuestro propio.
Ventajas:
- Aprenderás de verdad a pelearte con un script en bash al tener que programarlo y entender la lógica y comandos que se usan.
- Verás cosas que no funcionan (y explicaremos porqué) y cual es una solución (mi solución) al problema.
- Puedes adaptarlo a tus necesidades y gustos.
- Si eres nuevo en el scripting, verás algunos trucos o métodos diferentes en la programación bash
- Estarás orgulloso de haber programado tu propia aplicación.
- El propio script es una especie de resumen de trucos de comandos y operaciones en bash que os puede servir en el futuro para otras tareas.
Desventajas:
- Hasta el último artículo no disponeis de una versión totalmente operativa, salvo que vayais avanzando por vuestra cuenta en la programación de la aplicación.
Analizando TASKer
Empecemos por lo primero de todo, explicando qué es y qué no es , que podemos esperar del programa, cual es el producto minimo viable para considerarlo funcional, que mejoras podemos incluirle, etc...
¿Qué es TASKer?
TASKer es un script desarrollado en bash para gestionar desde la terminal las tareas personales o las tareas de un grupo de trabajo de una forma simplificada siguiente un esquema similar al "Cuadrante de Eisenhower".
Qué no es TASKer
No es una aplicación profesional, ni sigue una metodología de gestión de tareas o grupos.
No es una aplicación gráfica.
Que podemos esperar de TASKer
Se trata de una herramienta para la terminal, de uso rápido que permita anotar tareas, organizarlas y posteriormente repartirlas entre los miembros de un grupo de trabajo.
Tiene que funcionar el cualquier terminal que use bash: cualquier PC con GNU/Linux, PC con WSL, o incluso un Móvil o Tablet con Termux.
Tiene que ser fácil de leer su código, modificarlo y adaptarlo a las necesidades personales de cada uno.
Producto Mínimo Viable (MVP de TASKer)
Para que consideremos que la aplicación es operativa y podamos comenzar a usarla, TASKer debe de poder realizar lo siguiente:
- Capturar y almacenar nuevas Tareas
- Organizar/mover las tareas recibidas a los apartados o cuadrantes que deseemos
- Repartir las tareas de los cuadrantes a los miembros del grupo
- Permitir marcar como "cerrada" cualquier tarea
- Listar todas las tareas y su estado.
Mejoras que podemos incluir en Tasker
Algunas mejoras las abordaremos durante la realización de este curso-tutorial, otras las dejaremos para mejoras futuras
- Aspecto visual, colores, logotipo de la aplicación.
- Tareas en formato Markdown.
- Generacion en formato PDF de las Tareas.
- Estadística de las Tareas.
- Otras que puedan surgir en la programación...
Comenzamos...
Con todo lo anterior en mente, debemos pensar en cómo vamos a trabajar con nuestro código.
Protegiendo nuestro código
Lo primero que te recomiendo es que programes usando Git.
Git es un sistema de control de versiones que realiza un seguimiento de los cambios en los archivos.
De esta forma ante cualquier problema, error o borrado de tu código puedes volver a una versión previa del mismo.
Puedes ir escribiendo el código desde cualquier sitio, desde tu PC de sobremesa, tu portatil o incluso desde una Tablet con Termux. luego sólo tienes que subir el código al repositorio git
y lo tendras disponible desde cualquier sitio.
Para entender qué es GIT, te recomiendo leer el artículo:

Entender Git en minutos
Editor para el código
Puedes usar cualquier editor de texto con el que te sientas cómodo.
Si lo vas a programar desde la terminal te recomiendo usar Vi/Vim, lo tienes disponible en cualquier versión de Linux, puedes leer un artículo sobre Vim para aprender como usarlo:

Si vas a trabajar en un equipo que disponga de escritorio gráfico, te recomiendo usar VS Codium
la es versión de VS Code de Microsoft pero sin el tracking/seguimiento.

Curiosidades sobre TASKer: En mi caso, el 90% del código de la aplicación la he programado en los trayectos realizados en Cercanías, todo ello escritor usando Vim, desde la terminal en una Tablet Android en la que tengo instalado Termux.
Si además quieres aumentar tu productividad y comodidad, puedes instalar Tmux
en la Tablet para trabajar en modo multi-ventana desde un único terminal.

Puedes aprender a usar Tmux
para sacarle todo el provecho leyento el siguiente artículo artículo:
Vamos con la Idea
Antes de ponerme con el desarrollo, necesito un esquema previo de los procesos mínimos y que pasa en ellos para luego poder pasar ese esquema a una primera version de código....

Básicamente obtengo los siguientes procesos de entrada y salida para cada acción del programa:
- Alta de Tareas:
- Entrada: Cualquier tipo de tarea que pueda surgir o recibir, ya sea por email, chat, videoconferencia, gestor de incidencias o teléfono, tengo que poder capturarla con el teclado de forma muy rápida.
- Salida: Se crea el ficheroentrada
y se almacena en él la fecha y el texto de la tarea de forma secuencial en texto sin formato. - Organizar Tareas:
- Entrada: Se muestra linea a linea el ficheroentrada
y se captura las teclasD
,S
,U
yF
para repartir en el cuadrante de TareasDSUF
(Diaria/Semanal/Urgente/Futuras)
- Salida: Cuatro ficherosdia
,semana
,urgentes
yfuturas
las tareas organizadas desaparecen del ficheroentrada
- Repartir Tareas:
- Entrada: Usaremos un fichero auxiliargrupo
del que obtengo los miembros del grupo y suletra rápida
para repartir cada una de las tareas de cada fichero DSUF.
- Salida: Se modifica cada ficherodia
,semana
,urgentes
yfuturas
y se incluye al miembro encargado de resolver esa tarea. - Cerrar Tareas:
- Entrada: Se recorren todos los ficherosDSUF
y sus tareas. Se captura la teclaC
si se quiere marcar cada tarea como cerrada.
- Salida: Se modifica cada ficherodia
,semana
,urgentes
yfuturas
y se incluye[X]
para tareas cerrada y se deja como[ ]
para tareas sin cerrar. - Ver Tareas:
- Entrada: Se leen los ficheros DSUF y se muestra el contenido de cada fichero forma paginada.
- Salida: No se generan ficheros.
Codificando
Para tener una mayor claridad y separar código vamos a crear un directorio al que llamaremos lib
que usaremos a modo de almacen de las "librerías y funciones de nuestro código":
Crearemos el directorio necesario:
mkdir -p lib
La estructura quedará así:
directorio_TASKer/
└── lib
Nuestro primer fichero que va a ir dentro del directorio lib
va a ser un fichero en el que pongamos todas las variables que vamos a usar en nuestro programa.
Le vamos a llamar Tvariables
para abreviar Tasker + variables
Creamos el fichero simplemente con el comando touch lib/Tvariables
y lo editamos para incluir por ejemplo la siguiente variable globales, para poder definir el nombre del fichero de entrada en que guardaremos las tareas nuevas:
# FICHERO DE VARIABLES GLOBALES DE LA APLICACION
# Para ficheros
FE="TASKer.Entrada"
Nota: si no te gusta el nombre del fichero, simplemente cambia el nombre del fichero por el tuyo personalizado.
Ahora creamos el programa principal, al que yo voy a llamar TASKer
simplemente creamos el fichero, le damos permisos de ejecución.
touch TASKer
chmod +x TASKer
Luego lo editamos e incluimos el siguiente código:
#!/bin/bash
# Incluir variables
. lib/Tvariables
Truco: Includes en Bash
En vez de tener en el código principal del programa todas las variables que voy a necesitar, que pueden ser muchas y me darían un código final poco legible, las dejo en un fichero externo y las cargo/incluyo en la ejecución del código del programa principal.
Puedes leer la explicación de esto en el artículo de mi blog:

Includes en bash
Editamos el script TASKer e incluimos algo más de código para depurar que es correcto y se leen las variables mediante el include:
#!/bin/bash
# Incluir variables
. lib/Tvariables
# Depuracion - borrar despues
echo "El fichero de entrada es: $FE"
Lo ejecutamos escribiendo en la terminal: ./TASKer
Y comprobamos que efectivamente ha leido el valor del fichero externo de variables:
./TASKer
El fichero de entrada es: TASKer.Entrada
Truco: No es necesario la extensión.
Te abrás dado cuenta que al script bash no le he incluido la extensión .sh y Linux lo sabe ejecutar correctamente.
En Linux se sigue la máxima de "El hábito no hace al monje".
Al igual que el refrán, se recomienda no juzgar por su aspecto externo, pues no siempre el exterior corresponde al interior.
En Linux/Unix se usa Shebang
, que en la jerga linuxera, es el nombre que recibe el par de caracteres #! que se encuentran al inicio de los programas ejecutables que son interpretados. También lo puedes encontrar con el nombre de hash-bang
o sharpbang
.
Estos caracteres indican al intérprete de comandos que programa es necesario para ejecutar el código interno del programa.
El nombrar al script como TASKer
en vez de TASKer.sh
le da un toque más profesional 🧑🏻💻
Logotipo
Antes de ponernos a tope con el código, el disponer de un logo para el programa hace que luzca más profesiónal, es un capricho chulo que nos vamos a permitir para que cada vez que lancemos una prueba inicial veamos lo bonito que va a quedar....
Podemos hacer mediante ASCII-Art nuestro propio logotipo o usar algun comando Linux que nos simplifiquen el proceso.
Disponemos de 3 aplicaciones básicas que nos permiten hacer Arte ASCII de forma sencilla:
- banner: Puedes instalarlo mediante
sudo apt -y install sysvbanner
- toilet ó figlet: Puedes instalarlo con el comando sudo
apt -y install toilet
Uso y resultado de cada uno de ellos:
$ banner TASKer
####### # ##### # #
# # # # # # # ###### #####
# # # # # # # # #
# # # ##### ### ##### # #
# ####### # # # # #####
# # # # # # # # # #
# # # ##### # # ###### # #
figlet
Resultado con "banner"
$ toilet TASKer
mmmmmmm mm mmmm m m
# ## #" " # m" mmm m mm
# # # "#mmm #m# #" # #" "
# #mm# "# # #m #"""" #
# # # "mmm#" # "m "#mm" #
Resultado identico con "figlet" y "toilet"
banner
es una aplicación que viene de la época de los Unix System V, de los años 1983, y emula exactamente lo que hacía el código de aquella época, no se le puede pedir más.
Sin embargo toilet / figlet
es una aplicación moderna que puede hacer lo mismo que banner y algo más.... así que vamos a aprovecharla para hacer nuestro logo:

Como la aplicación es de consola y además debe de ser portable, lo que vamos a hacer es generar el logotipo de la aplicación y almacenarlo para poder usarlo sin tener que disponer del programa toilet
en ningun equipo.
Para ello vamos a guardar la salida en un fichero al que por ahora vamos a llamar logo
:
toilet -f smblock --filter border:metal TASKer > logo
Si ejecutamos un cat logo
veremos que se muestra el logotipo perfectamente.
Sin embargo si abrimos con un editor el fichero logo
veremos algo totalmente "extraño":

Esto no es más que un texto en el que los colores se indican mediante caracteres de escape.
Puedes ver el artículo completo en el que te cuento como usar los colores en bash:

Colores en bash
Lo que vamos a hacer es guardar todo este conjunto de caracteres de texto como una variable en nuestro fichero de variables Tvariables
, lo vamos a hacer de una forma sencilla:
echo 'LOGOTIPO="' >> lib/Tvariables
toilet -f smblock --filter border:metal TASKer >> lib/Tvariables
echo '"' >> lib/Tvariables
Incluir variable LOGOTIPO
Nuestro fichero de variables se verá ahora así:

Vamos a crear nuestro primer fichero de Librería TLibPantalla
y nuestra primera función Logo()
para mostrar a nuestro antojo el logotipo.
touch lib/TLibPantalla
Lo editamos y escribimos
# LIBRERIA DE FUNCIONES PARA DIBUJAR EN PANTALLA
function Pantalla::Logo() {
clear
echo -e "$LOGOTIPO"
}
Truco: echo -e
El comando echo
permite el modificador -e
que permite interpretar los caracteres de escape y en nuestro caso dibujar con colores.
Recuerda además que la función la hemos creado con el nombre Pantalla::Logo
y debemos usarla con su nombre completo, hacemos eso simplemente para saber luego en que fichero/librería está cada función.
Modifiquemos el programa principal TASKer
para hacer uso de la librería mediante un include y usar nuestra nueva función:
#!/bin/bash
# Incluir variables
. lib/Tvariables
# INCLUDES ;-D
. lib/TLibPantalla
# --- Main ---
Pantalla::Logo
Si ejecutamos el programa veremos nuestro logo a todo color y sin necesidad de ningun programa externo para dibujarlo.
El Menú
Vamos a necesitar dibujar un menú para que sepamos que tarea estamos realizando o que tecla hay que pulsar para ejecutar una acción.
Nuestro primera aproximación al menú de la aplicación va a mostrar algo así:
MENU PRINCIPAL:
[A] ALTA DE TAREAS
[O] ORGANIZAR TAREAS
[R] REPARTIR TAREAS AL GRUPO
[C] CERRAR TAREAS
[V] VER LAS TAREAS
[F] FIN DEL PROGRAMA TASKer
Pues vamos a modificar la libreria de pantalla lib/TLibPantalla
para incluir la funcion que pinte el menú:
function Pantalla::PintarMenu() {
echo "MENU PRINCIPAL:
[A] ALTA DE TAREAS
[O] ORGANIZAR TAREAS
[R] REPARTIR TAREAS AL GRUPO
[C] CERRAR TAREAS
[V] VER LAS TAREAS
[F] FIN DEL PROGRAMA TASKer
"
}
Vamos a crear en el mismo fichero otra funcion de nombre Menu
que va a realizar lo siguiente, se ejecuta en un bucle infinito del que solo sale si se pulsa una de las teclas del menú.
- Primero muestra el logotipo
- Despues pinta el menu
- Espera hasta que se pulsa una tecla correcta
La función del menú quedaría así:
function Pantalla::Menu() {
while true
do
Pantalla::Logo
Pantalla::PintarMenu
# Esperamos que pulse una tecla.
# la pulsacion se guarda en la variable $opcion
read -n 1 -s -p " -- Pulse inicial de opción -- " opcion
# Tecla pulsada a MAYUSCULAS
opcion=$(echo $opcion|tr [:lower:] [:upper:])
valoresMenu="AORCVF"
# Comprobamos si la tecla esta en el grupo de teclas permitidas
valido=$(echo $valoresMenu |grep -c -v "$opcion" -- 2>/dev/null|awk '{print $1}')
if [ "$valido" == "0" ]
then
# Dejamos salir porque es una Tecla del menu
break
else
# Mensaje en pantalla de error y esperamos 0.75 segundo para que se vea
echo ""
echo "** Opcion incorrecta **"
sleep 0.75
fi
done
}
Modificamos el fichero TASKer
quedando así:
#!/bin/bash
# Incluir variables
. lib/Tvariables
# INCLUDES ;-D
. lib/TLibPantalla
# --- Main ---
Pantalla::Menu
Comprobamos su funcionamiento y vemos que se queda ejecutandose en pantalla hasta que pulsemos una de las opciones correctas del menú:

Vamos a darle más importancia a las teclas que se deben de pulsar, vamos a crear una variable Global el valor de la tecla pulsada opcion
y además en el menú vamos a "colorear" la teclas que hay que pulsar.
Como seguramente necesitemos estos colores en más partes del programa, vamos a crear unas variables para definir y usar los colores de forma sencilla con el comando echo -e
Al fichero lib/Tvariables
le incluimos las siguientes líneas:
....
# Pulsaciones del Menu
opcion=""
# Para Colores en Pantalla
ColorNormal="\e[0m"
Rojo="\e[31m"
RojoIntenso="\e[1;31m"
Verde="\e[32m"
VerdeIntenso="\e[1;32m"
Amarillo="\e[33m"
AmarilloIntenso="\e[1;33m"
Azul="\e[34m"
AzulIntenso="\e[1;34m"
Y modifico la función PintarMenu
para que coloree cada tecla:
function Pantalla::PintarMenu() {
echo -e "$Azul MENU PRINCIPAL:$ColorNormal
["$VerdeIntenso"A"$ColorNormal"] ALTA DE TAREAS
["$VerdeIntenso"O"$ColorNormal"] ORGANIZAR TAREAS
["$VerdeIntenso"R"$ColorNormal"] REPARTIR TAREAS AL GRUPO
["$VerdeIntenso"C"$ColorNormal"] CERRAR TAREAS
["$VerdeIntenso"V"$ColorNormal"] VER LAS TAREAS
["$VerdeIntenso"F"$ColorNormal"] FIN DEL PROGRAMA TASKer
"
}
Si además modificamos la línea cuando se pulsa una tecla errónea, para que mueste el texto en rojo:
echo -e " "$Rojo"** Opcion incorrecta **"$ColorNormal"\n"
La nueva versión del menú se ve así:

Alta de Tareas
Vamos a crear nuestra librería para gestionar las tareas mediante funciones para simplificar el código, la vamos a crer también dentro del directorio lib
y la vamos a llamar TLibTareas
En esta librería vamos a crear una función para ejecutar una acción dependiendo de la tecla que pulsó anteriormente que tenemos almacenado en la variable global $opcion
, esta función se va a llamar Tareas::Accion
# LIBRERIA DE FUNCIONES PARA REALIZAR TAREAS
function Tareas::Accion() {
case $opcion in
A)
Tareas::Alta
;;
O)
Tareas::Ordenar
;;
R)
Tareas::Repartir
;;
C)
Tareas::Cerrar
;;
V)
Tareas::Ver
;;
F)
Tareas::Fin
;;
esac
}
Además vamos a crear la función Tareas:Alta
que el la función que grabará cada una de las tareas en el fichero de entrada de tareas:
function Tareas::Alta() {
# Formato: "Y-m-d Texto_de_la_tarea"
FICH="$FE"
while true
do
Pantalla::PintarPedirAlta
read tarea
if [ "$tarea" == "" ] ; then
break
else
echo "$DIA $tarea" | tr [:lower:] [:upper:] >> $FICH
fi
done
}
Esta función tiene las siguientes peculiaridades:
- Se ejecuta en bucle, solo se sale de la función si se pulsa INTRO sin ningún tipo de texto
- El texto escrito se almacena en la variable
$tarea
al pulsar INTRO, el texto se guarda en el fichero que indicamos en la variable$FE
del ficheroTVariables
- Cada línea de texto escrito se le incluye por delante de él, el día en formato YYYY-MM-DD (año-mes-dia), esto nos servirára posteriormente para realizar busquedas y ordenaciones por fecha de una forma fácil.
- El valor de
$DIA
lo vamos a tener definido como una variable en el ficheroTvariables
de la siguiente forma:
# Dia / Hora
DIA=$(date +%Y-%m-%d)
- Pintaremos una pantalla con texto mediante la función
PintarPedirAlta
para indicar lo que hay que hacer.
Esta función va en la libreríaTLibPantalla
:
function Pantalla::PintarPedirAlta() {
Pantalla::Logo
echo -e " "$Azul"ESCRIBA LA TAREA + INTRO"$ColorNormal
echo -e " (INTRO sin texto para Salir)\n"
}
También vamos a crear la función para finalizar el programa, que es una de las más sencillas y va en la librería TLibTareas
:
function Tareas::Fin() {
clear
exit 0
}
Por ultimo editamos TASKer
el fichero principal de la aplicación para realizar el include de la nueva libreria TLibTareas
el código debería ser el siguiente:
#!/bin/bash
# Incluir variables
. lib/Tvariables
# INCLUDES ;-D
. lib/TLibPantalla
. lib/TLibTareas
# --- Main ---
while true
do
Pantalla::Menu
Tareas::Accion
done
Cierto, lo has visto.... otro bucle para que no salgamos del menu...
Si ahora ejecutamos el programa con el comando ./TASKer
nos quedamos en el menún de forma indefinida hasta que pulsemos la tecla F
para finalizar el programa.
Si pulsamos la tecla A
veremos nuestro menú de Alta de Tareas:

Si pulsamos INTRO
salimos sin realizar ninguna acción.
Pero si escribimos texto y pulsamos INTRO

Al escribir texto se creará el fichero TASKer.Entrada
en el que se guardarán todas las tareas:

Puedo entrar y salir del programa y probar a incluir nuevas tareas:

Código de la parte 1
Con esto finaliza la primera parte de la programación de TASKer, te recomiendo que edites por tu cuenta cada uno de los ficheros, que pruebes distintas opciones de comandos, que averigues el porque de los bucles while true
y como puedes modificarlo o simplificarlo.
Seguramente conozcas el comando select option in ...
que te permite gestionar un menú, prueba a modificar el código usando este comando para ver si te resulta más cómodo o no.
Practica y juega con el código sin miedo.
Si por algún motivo algo no te funciona correctamente, te dejo las versiones del código de la parte 1 para que los puedas descargar.
La estructura y ubicación de los ficheros deberá de ser la siguiente:
.
├── lib
│ ├── TLibPantalla
│ ├── TLibTareas
│ └── Tvariables
└── TASKer
2 directories, 4 files
Nos vemos en la segunda parte en la que Organizaremos las Tareas y tendremos que leer y procesar la "Bandeja de entrada de Tareas"