Apuntes mientras aprendo sobre software y computadoras.

programación

Cómo usar la técnica Pomodoro en Linux usando Bash

En este apunte práctico mi objetivo es aprovechar la técnica Pomodoro en Linux, sin usar para eso software o aplicaciones externas.

Todo esto que sigue a continuación vamos a tener que realizarlo por medio de un código escrito en Bash.

Pero una buena pregunta antes de continuar es…

¿Qué es la técnica Pomodoro?

Bueno, se trata de una técnica que puede servirnos para utilizar mejor el tiempo que vamos a dedicarle a una tarea. El concepto fue credo por Francesco Cirillo.

Mi super resumen: el objetivo es dividir una tarea en intervalos fijos de tiempo, separando cada intervalo por una pequeña pausa de descanso.

Creo que se va a entender mejor revisando un caso:

Me concentro en una tarea, por ejemplo la de escribir este texto, por quince minutos con una dedicación completa.

Luego tomo un par de minutos de descanso. Y terminada la pausa vuelvo a trabajar por otros quince minutos. Acto seguido viene otro micro recreo.

Y repito este ciclo hasta completar mi objetivo, agregando en el medio algunos periodos de descanso más largos.

El código que sigue a continuación va a ayudarme a cronometrar esos intervalos, con la menor cantidad de distracciones posibles.

¿Qué necesita este programa para ser efectivo?

Bueno, como ya dije, lo que necesito es poder concentrarme en una tarea por pequeños intervalos de tiempo.

Si bien para poder estar enfocado necesito aplicar fuerza de voluntad, hay algo para lo que usar solamente fuerza de voluntad pocas veces alcanza. Y ese algo es la internet.

Para que este programa funcione, necesito desconectarme de la red por al menos quince minutos sin interrupciones. Y luego el programa tiene que volver a conectarme.

Y es importante que el programa sea difícil de “apagar” para no buscar hacer trampa reconectando la red.

Lo verdaderamente complicado es que en Linux no existe nada que que no pueda ser contrarrestado, si cuento con los permisos suficientes para realizar la acción.

Lo que quiero entonces es que se vuelva muy molesto interrumpir el programa antes de que el mismo termine.

Una primera versión del código

Con esta combinación de comandos puedo conseguir, al menos en principio, lo que estoy buscando:

nmcli networking off & sleep 900; nmcli networking on

Se trata de dos comandos que se activan en conjunto (por medio del conector “&”).

Por un lado tengo el comando:

nmcli networking off

Según su manual:

NAME
nmcli – command-line tool for controlling NetworkManager

manual de nmcli

Esto es, nmcli es una herramienta de la terminal que sirve para controlar el manager de conexiones.

Al apagar y prender nmcli con “on/off” desactivo todas las interfaces que manejan la conexión a la red. Y por supuesto desactivando el modem pierdo la tan deseada conexión a internet en la máquina.

Puede que esta no sea la mejor opción para desconectarme, pero cumple con lo que estoy buscando. No puedo navegar mientras esta opción se encuentra apagada.

Lo siguiente es el comando:

sleep 900

Sobre este comando, el manual me dice:

NAME
sleep – delay for a specified amount of time

Manual de sleep

Según entiendo lo que hace el comando sleep es dejar la terminal pausada (o en espera) para ejecutar el siguiente comando por la cantidad de tiempo que especificamos.

En este caso ese tiempo es 900 segundos o lo que es lo mismo, 15 minutos.

Es verdad que se le puede especificar el tiempo en minutos usando la opción “-m”… pero es más fácil de este modo.

Entonces con esa cadena de comandos: me desconecto de internet, la terminal espera quince minutos y luego vuelvo a re conectarme por medio de:

nmcli networking on

Bastante bien. Pero puedo mejorar este programa un poco más.

Versión 2.0: Ahora con más molestias

Un problema del código anterior es que puedo interrumpirlo muy fácilmente.

Si en cualquier momento lo cancelo y vuelvo a activar “nmcli”, voy a volverme a conectar a internet. Acto seguido voy a estar navegando redes sociales en lugar de completar mi objetivo y la técnica Pomodoro no va a servir de nada.

Para esta nueva versión lo que voy a tener que hacer es crear un nuevo documento. Este documento lo voy a llamar “pomodoro.sh” y va a ser la base para nuestra aplicación.

El código que voy a usar es el siguiente:

#!/bin/bash
# Pomodoro rápido para la terminal
# Este código deshabilita la interfaz de red por un intervalo de 15 minutos.

# Reviso si el archivo de bloqueo existe o voy a crearlo.
archivo_bloqueo="/tmp/deshabilitar_red.lock"
if [ -f "$archivo_bloqueo" ]; then
  echo "El archivo de bloqueo ya existe."
  exit 1
else
  touch "$archivo_bloqueo"  
fi

# Establezco permisos más restrictivos en el archivo de bloqueo.
chmod 400 "$archivo_bloqueo"

# Deshabilito la interfaz de red.
nmcli networking off

# Muestro una cuenta regresiva de 15 minutos en la terminal.
for i in {900..1}; do
  printf "\rLa interfaz de red se volverá a habilitar en %d segundos..." "$i"
  sleep 1
done

# Vuelvo a habilitar la interfaz de red.
nmcli networking on
if [ -f "$archivo_bloqueo" ]; then
  # modifica el nombre del archivo creado al principio para borrarlo.
  chmod 600 "$archivo_bloqueo"
else
  echo "El archivo de bloqueo no existe."
fi
# Elimino el archivo de bloqueo.
rm "$archivo_bloqueo"

Ahora necesito cambiar los permisos para poder ejecutar el código:

chmod +rx pomodoro.sh

Y para ejecutar el programa escribo en la terminal:

./pomodoro.sh

Para usar el programa necesito estar en el directorio donde se encuentra el archivo.

Si querés saber cómo convertir un programa creado en Bash en comando personalizado, te recomiendo leer esta entrada.

Nuestro programa tiene dos características principales para revisar:

Cuando comienza el script, tengo el siguiente bloque de código:

# Reviso si el archivo de bloqueo existe o voy a crearlo.
archivo_bloqueo="/tmp/networking_disabled.lock"
if [ -f "$archivo_bloqueo" ]; then
    echo "El archivo de bloqueo ya existe."
    exit 1
else
    touch "$archivo_bloqueo"
fi

Esto revisa si el archivo “deshabilitar_red.lock” existe en la carpeta “/tmp/”.

Si el archivo no existe lo crea y continua. Pero si el archivo existe, el programa termina.

La idea es que este archivo impide utilizar el programa varias veces seguidas. Mientras el archivo “deshabilitar_red.lock” exista no puedo tratar de volver a conectar la red usando de nuevo el mismo código.

Al mismo tiempo, transforma en una molestia el acto de cancelar el código mientras funciona, porque de hacerlo tendría que ir hasta el directorio “tmp” para buscar el archivo generado y borrarlo manualmente.

Y los permisos del archivo son modificados con:

chmod 400 "$archivo_bloqueo"

Por lo que para borrarlo necesito buscarlo y si o si utilizar el permiso sudo. Doblemente molesto.

Al terminar el código, otra función se va a encargar de borrar el documento para empezar desde cero cuando vuelva a invocar al programa.

2- Ahora el programa me dice que tengo 900 segundos para enfocarme en mi objetivo.

for i in {900..1}; do
 printf "\rLa interfaz de red se volverá a habilitar en %d segundos…" "$i"
 sleep 1
done

Con esto en la terminal me aparece un mensaje que me indica en cuantos segundos voy a volver a tener internet.

Este recordatorio hace que se me vuelva más fácil concentrarse, porque nos recuerda que es un intervalo corto y nos ayuda a mantenernos enfocados.

Y la función encargada en volver a reactivar la red es:

# Vuelvo a habilitar la interfaz de red.
nmcli networking on
if [ -f "$archivo_bloqueo" ]; then
  # modifica el nombre del archivo creado al principio para borrarlo.
  chmod 600 "$archivo_bloqueo"
else
  echo "El archivo de bloqueo no existe."
fi
# Elimino el archivo de bloqueo.
rm "$archivo_bloqueo"

Esto vuelve a establecer la red con el comando nmcli, y borra el archivo que habiamos creado para no volver a ejecutar el programa múltiples veces.

Problemas de la versión 2.0:

Creo que el verdadero problema que tiene el programa es que puede deshabilitarse por medio del comando:

nmcli networking on

Si utilizo ese comando desde otra terminal, vuelvo a “prender” internet.

Eso se podría resolver por ejemplo si cambiamos el nombre del comando “nmcli”, para que no pueda saber cómo invocarlo.

Antes de continuar, tengo que conocer donde se encuentra el directorio con el comando “nmcli”.

Eso lo hago con el comando “which” que enseña el camino al directorio:

which nmcli
/usr/bin/nmcli

Entonces si cambio el nombre del directorio por algo compilado, de esta forma:

sudo mv /usr/bin/nmcli /usr/bin/sfsfirjjjkxoyuojir5rehtg

Al hacer eso necesitaría escribir “sfsfirjjjkxoyuojir5rehtg” para invocar al comando “nmcli”, lo que convertir la actividad en algo ultra molesto.

Luego podría deshacer el cambio con:

sudo mv /usr/bin/sfsfirjjjkxoyuojir5rehtg /usr/bin/nmcli

¿Por qué esta solución no es una buena idea?

Aunque puede funcionar, también significa modificar un directorio importante del sistema.

Por eso aunque creo que puede funcionar, prefiero no hacerlo y confiar al menos en un grado en mi autocontrol para no reiniciar el contador y volver a activar internet.

O también podemos revisar otra versión del programa.

Versión 2.1: buscando más pomodoro

Esta es una nueva versión del código.

La principal diferencia con la versión anterior es que ahora casi todo se encuentra organizado en funciones, para que resulte más fácil de entender y mejorar en el futuro.

#!/bin/bash

# Pomodoro 2.1 rápido para la terminal
# Este código deshabilita la interfaz de red por un intervalo x de minutos.

# modo de uso: pomodoro.sh argumento (número entero representando minutos) 

lockfile="/tmp/pomodoro_network.lock"


# función para borrar el archivo de bloqueo
function cleanup() {
    rm -f "${lockfile}"
}

# Reviso si el archivo de bloqueo existe o voy a crearlo.
function create_and_check_lockfile() {
    if [ -e "${lockfile}" ]; then
        echo "El programa ya se encuentra en ejecución."
        exit 1
    fi
    touch "${lockfile}" && chmod 600 "${lockfile}" || exit 1
}

# Interrumpir la red por medio del comando nmcli
function disable_network() {
    local retries=2
    local sleep_time=5

    for ((i=1; i<=retries; i++)); do
        nmcli networking off
        if [ $? -eq 0 ]; then
            # Mostrar notificación del comienzo del programa
            notify-send "Pomodoro comienza" "Internet fue interrumpida para que puedas dedicarte a tu tarea."
            echo "La interfaz de red fue interrumpida."
            return 0
        else
            echo "Error: internet no pudo ser desconectada (intento núm: $i/$retries). Volver a intentar en $sleep_time segundos..."
            sleep $sleep_time
        fi
    done

    echo "Error: No se pudo desconectar internet luego de $retries intentos."
    return 1
}

# conectar la red por medio del comando nmcli
function reenable_network() {
    local retries=2
    local sleep_time=5

    for ((i=1; i<=retries; i++)); do
        nmcli networking on
        if [ $? -eq 0 ]; then
            # Mostrar notificación para avisar que ya hay internet
            notify-send "Pomodoro terminado" "Podés descanzar unos minutos antes de volver a tu tarea."
            echo "La interfaz de red fue habilitada."
            return 0
        else
            echo "Error: internet no pudo ser establecida (intento núm: $i/$retries). Volver a intentar en $sleep_time segundos..."
            sleep $sleep_time
        fi
    done

    echo "Error: No se pudo conectar la red luego de $retries intentos."
    return 1
}

# Revisar por argumentos de tiempo
if [ $# -eq 0 ]; then
    WAIT_TIME=10
else
    WAIT_TIME=$1
    if ! [[ "$WAIT_TIME" =~ ^[0-9]+$ ]]; then
        echo "Agregar número entero $0 <tiempo en minutos>"
        exit 1
    fi
fi

# convertir el tiempo en segundos para sleep
WAIT_TIME=$(( WAIT_TIME * 60 ))

# Comienza el programa llamando las funciones
create_and_check_lockfile
if [ $? -ne 0 ]; then
    exit 1
fi

echo "$(date +"%Y-%m-%d %H:%M:%S") - Deshabilitar conexión de red..."
if ! disable_network; then
    echo "Error: no se pudo desconectar la red"
    cleanup
    exit 1
fi

echo "$(date +"%Y-%m-%d %H:%M:%S") - Interfaz de red desconectada. La conección va a volver en $WAIT_TIME segundos..."
sleep $WAIT_TIME

if ! reenable_network; then
    echo "Error: No se pudo habilitar la red."
    cleanup
    exit 1
fi

cleanup

Otra mejora es que el programa permite ahora que le pasemos la cantidad de minutos que queremos pasar con la técnica Pomodoro. Y si no le damos ningún argumento, el tiempo mínimo sin conexión es de 10 minutos.

# Revisar por argumentos de tiempo
if [ $# -eq 0 ]; then
    WAIT_TIME=10
else
    WAIT_TIME=$1
    if ! [[ "$WAIT_TIME" =~ ^[0-9]+$ ]]; then
        echo "Agregar número entero $0 <tiempo en minutos>"
        exit 1
    fi
fi

# convertir el tiempo en segundos para sleep
WAIT_TIME=$(( WAIT_TIME * 60 ))

Todavía tenemos algo similar a un problema: la red puede ser restablecida utilizando la interfaz gráfica o el comando nmcli desde otra terminal.

De todas maneras lo mejor es no pensar en eso, una vez que marcamos un lapso para dedicarnos en algo tenemos que resolver cumplir con ese lapso.

Lo más importante para que todo esto funcione: necesitamos establecer un objetivo razonable. Y necesitamos luego dedicarle a ese objetivo un tiempo sin distracciones para poder entender mejor lo que estamos haciendo.

Conclusión:

Este es mi primer intento para crear un programa Pomodoro en la terminal de Linux.

Tal vez puedas usar el código como base para crear algo mejor. Incluso probar agregarle mejoras con una inteligencia artificial permite construir algo complejo de una forma bastante rápida.

En lo que a mi respecta, aprendí mucho creando todo. Espero que te sirva de utilidad, y si te interesa decir algo sobre esto espero tus comentarios o mensajes por correo electrónico.

La seguimos en el siguiente apunte.

Recursos: