“Esta es la filosofía de Unix: escribir programas que hagan una cosa y la hagan bien. Escribir programas para trabajar juntos. Escriba programas para manejar flujos de texto, porque esa es una interfaz universal.”

Douglas McIlroy

Bash (y sus derivados) han estado presentes en la historia de la computación desde hace más de 30 años y aunque nunca ha sido un lenguaje considerado pivote para la innovación, ha ayudado a muchos desarrolladores a hacer de su trabajo algo menos monótono.

Es tanto un intérprete de líneas de comando (shell de Unix) como un lenguaje scripting para entornos (en principio) basados en unix. Como la mayoría de los shells de Unix, es compatible con el wildcard matching en nombres de archivos, pipes, sustitución de comandos, variables y estructuras de control para pruebas de condición e iteración. Las palabras clave, la sintaxis, las variables de alcance dinámico y otras características básicas del lenguaje se copian de sh. Otras funciones, por ejemplo, el historial, se copian de csh y ksh. Bash es un shell compatible con POSIX, pero con varias extensiones.

El nombre del shell es un acrónimo de Bourne Again Shell, un juego de palabras con el nombre del shell Bourne al que reemplaza y la noción de “nacer de nuevo” (en inglés).

Esta es una pequeña introducción a un proyecto que llevo algún tiempo trabajando de forma privada, para poder organizar funciones, aliases y variables de entorno de una forma que tenga sentido y pueda ser escalable.

Motivación

Hace varios años que uso la terminal bash para acortar el tiempo de trabajo que se hace regularmente con una interfaz gráfica de usuario, pero a medida que los proyectos en los que trabajo crecen en complejidad, la cantidad de comandos que debo usar para compilar, empaquetar y subir código a dispositivos de prueba (trabajo con redes) se incrementa también. Creo que, como he descrito en un artículo anterior, se necesita mucha energía creativa para trabajar en el desarrollo de software y cada vez que perdemos unos 10seg intentando escribir o recordar algún comando complejo en la terminal, perdemos algo de esa energia que nos ayuda a tomar decisiones que sí son relevantes.

El manejo del tiempo es crucial para ser un desarrollador exitoso, no solo para entregar el trabajo a tiempo, sino para asegurar la calidad y la optimización cuando se soluciona un problema.

La matemática no falla en ese sentido, si pierdo unos 8-10 segundos cada vez que tengo que probar un cambio, asumiendo que demoro unos 10-15min en escribir parte del código al año estaría perdiendo entre 20-29 horas al año solo haciendo trabajo mecánico y en proyectos como en el que trabajo estos números se pueden duplicar o triplicar.

Bash

Si, parece obsesivo-compulsivo contar este tiempo, pero el tiempo perdido en trabajo mecánico no es lo relevante, es el tiempo perdido en trabajo realmente creativo.

La idea

Fue muy simple, poder tener un proyecto que cumpliera con las siguientes características:

  • Ser escalable, permitiendo la separación de intereses (no es lo mismo tener funciones para networking que aliases para git).
  • Ser fácilmente exportable a otras plataformas (Sistemas operativos, servidores, raspberry pis, etc.), por lo que su procesamiento tiene que terminar en un solo archivo exportable (envrc)
  • Que su adaptabilidad fuera sencilla, que al necesitar agregar un comando o configuración nueva, su lugar tenga sentido desde el principio.

Organización de los archivos

La elección de la organización de los archivos no fue tan difícil cuando tenía la idea clara, el resultado fue el siguiente:

/
  config        # Carpeta para agregar todas las configuraciones que van en el ~/.config
    jrnl        # Ejemplo: Mi configuración por defecto para jrnl.sh
  env           # Carpeta en la que se almacenan de forma separada los scripts
    git.sh 
    global.sh
    media.sh
    network.sh
    personal.sh
  rc            # Configuraciones específicas para los archivos rc (de bc, la calculadora, por ejemplo)
    .vim
    .bc
    .vimrc
  Makefile      # Archivo que ayuda a construir el .envrc en instalar el entorno usando la herramienta make
  deploy        # Script que ayuda a subir el entorno de desarrollo a un host externo (rpi, servidor, etc.)

Scripts 

Los scripts están internamente organizados en: variables de entorno, aliases y funciones. Por ejemplo:

#!/bin/bash

###################################################################################
#                                                                                             #
#                                        PERSONAL ENV                                         #
#                                                                                             #
###############################################################################################

HUGO_SUPPORTED_LANGUAGES=("en" "es")
EMOTIONS_TEMPLATE=/home/jpgarcia/journals/templates/emotion.txt

#########################################################################################
#                                                                                             #
#                                        PERSONAL ALIASES                                     #
#                                                                                             #
###############################################################################################

alias hpost="hcontent posts"
alias hproject="hcontent projects"
alias emotion="jrnl emotions < $EMOTIONS_TEMPLATE && jrnl emotions -1 --edit"

#########################################################################################
#                                                                                             #
#                                        PERSONAL FUNCTIONS                                   #
#                                                                                             #
###############################################################################################

function hcontent() {
    if [ ! -d content/$1 ]; then
        echo "You must be on a HUGO project directory. Or create a base directory for content/$1."
    else
        if [ -z "$2" ]
        then
            echo "You have to provide a valid $1 slug (e.g: first-post)"
        else
            for VARIABLE in $HUGO_SUPPORTED_LANGUAGES
            do
                hugo new $1/$2/index.$VARIABLE.md
            done
        fi
        
    fi
}

function jrnl() {
    if [ $MACHINE = "Mac" ]; then
        /usr/local/bin/jrnl $@
    elif [ $MACHINE = "Linux" ]; then
        ~/.local/bin/jrnl $@
    else
        echo "jrnl.sh not found for $MACHINE"
    fi

  if [ -d ~/journals ]; then
        cd ~/journals
        if ! git diff-index --quiet HEAD --; then
            git add --all
            git commit -m "Log $(date)"
            git push origin master
        fi
        cd -
    fi
}

El toque especial

Lo que hace de este un proyecto interesante, es que no importa la cantidad de archivos, funciones y variables de entorno que se agreguen, con el uso del Makefile, todos estos archivos son convertidos en uno solo con una función ‘merge’:

merge_env:
  if [ ! -f envrc ]; then \
    echo "Generating envrc file"; \
    if [ -f /tmp/envrc ]; then \
      sudo rm /tmp/envrc; \
    fi; \
    touch /tmp/envrc; \
    cat env/*.sh > /tmp/envrc; \
  fi

Se genera de forma temporal el archivo envrc para luego ser copiado en el directorio preferido (en mi caso /etc).

Conclusión

Con bash se puede controlar virtualmente cualquier parte del sistema operativo que esté ejecutándose. Es la puerta de entrada al mismo, por lo tanto, tener un entorno de desarrollo personal y adaptable siempre hará que tu trabajo lo puedas hacer con la menor cantidad de actividades no-creativas posibles

El proyecto completo está publicado en github y puedes encontrarlo en el siguiente link.

Juan Pablo

Technical Lead

April 6, 2022