1. Scripting en Linux: Bash

Una de las herramientas más interesantes que tiene GNU/Linux es el uso de su terminal, la cual permite entre otras cosas ejecutar todas las instrucciones dentro del sistema operativo y, en consecuencia, automatizar las tareas que se requieran para una aplicación determinada.

GNU/Linux es uno de los sistemas operativos con mayor presencia en servidores, por lo cual saber utilizar su terminal es una herramienta muy útil para poder desplegar servicios. Para ello se requiere conocer el intérprete de esta consola (shell), siendo el más conocido y, en la mayoría de los casos, por defecto, Bash.

1.1. Fundamentos de shell

1.1.1. Intérpretes de shell

Existen varios intérpretes para comunicarse con la terminal de Linux, entre los principales se encuentran:

  • sh (Bourne Shell) Es el shell original utilizado en UNIX.

  • bash (Bourne Again Shell) Es el estándar para GNU/Linux es más intuitivo y confiable que sh.

  • csh (C shell) Con una sintaxis parecida al lenguaje de programación C.

  • tcsh (TENEX C shell) Es un superconjunto de csh, más amigable con el usuario y más rápido.

Para conocer los shells instalados en su sistema puede ejecutar lo siguiente en la terminal:

$ cat /etc/shells

Por otra parte, el shell por defecto por usuario se puede encontrar en el archivo /etc/passwd.

A lo largo de esta revisión, se empleará bash para todos los ejemplos.

1.1.2. Tipos de Shell

Puede ser interactivo o no interactivo, que se refiere a la ineracción entre el usuario y la shell y de login o no login, que se refiere al tipo de shell empleado para acceder a un sistema computacional.

Para invocar a un shell interactivo non-login con bash, basta con ejecutar:

$ bash

Al ejecutarlo lo que se va a cargar al inicio es un archivo ubicado en ~/.bashrc.

En el caso de un sell interactivo login con bash, es ligeramente diferente:

$ bash --login

o mediante

$ bash -l

Este shell autentica primero al usuario cargando los archivos del perfil de usuario ubicado en /etc/profile, luego ~/.profile antes de cargar ~/.bashrc.

1.1.3. Ejecutando comandos

Bash soporta tres tipos de comandos:

  • Comandos incluidos en Bourne: :, ., break, cd, continue, eval, exec, exit, export, getopts, hash, pwd, readonly, return, set, shift, test, [, times, trap, umask, unset.

  • Comandos incluidos en Bash: alias, bind, builtin, command, declare, echo, enable, help, let, local, logout, printf, read, shopt, type, typeset, ulimit, unalias.

  • Comandos especiales: cuando se ejecuta en modo POSIX, estos son :, ., break, continue, eval, exec, exit, export, readonly, return, set, shift, trap, unset.

1.1.4. Componentes de shell

1.1.4.1. Sintaxis de shell

Si la entrada no está comentada, el shell la lee y la separa en palabras y operadores utilizando las reglas de separación de bloques para definir el significado de cada uno. El proceso se puede distinguir en los siguientes pasos:

  • El shell lee desde un archivo, desde una cadena de texto o desde la entrada del usuario.

  • Divide la entrada en palabras y operadores.

  • Analiza y sustituye las partes en comandos simples y compuestos.

  • Bash realiza las expansiones necesarias.

  • Se realiza las redirecciones necesarias.

  • Se ejecutan los comandos.

  • Opcionalmente espera que termine de ejecutarse y retorna el estado de finalización.

1.1.4.2. Comandos de shell

Un comando de shell normalmente tiene la siguiente estructura:

$ comando argumento_1 argumento_2 argumento_3

Donde la primera palabra consituye el nombre del comando y las siguientes separadas por espacios son los argumentos para el comando, los cuales pueden ser en una cantidad variable.

1.1.4.3. Funciones de shell

Las funciones son básicamente una agrupación de varios comandos. Estas funciones pueden aceptar también una serie de argumentos variable como los comandos que ejecuta en su declaración.

1.1.4.4. Parámetros de shell

Un parámetro es una entidad que almacena información, que puede ser un nombre, un número o un valor especial. En el caso del shell, una variable es un parámetro que almacena una nombre.

1.1.4.5. Scripts de shell

Son ficheros que tienen una serie de comandos. Cuando se invocan con bash, lo primero que hace es buscarlo en el directorio actual, y en caso de no encontrarlo allí, en las ubicaciones declaradas en el PATH del sistema.

1.2. Scripts en Bash

Un script es un fichero que contiene un conjunto de instrucciones las cuales serán interpretadas por el shell. Para ejecutar un script es necesario o bien utilizar el intérprete como parte del comando de ejecución, o declarar el intérprete dentro del fichero.

En el primer caso, se puede invocar de la siguiente manera:

$ /bin/bash script.sh

Usualmente los scripts para bash o cualquier intérprete en general tienen la extensión .sh.

Por otra parte, en caso de requerir ejecutarlo directamente desde la terminal, sin especificar el intérprete en el comando, primero, debe dar permisos de ejecución al fichero mediante:

$ chmod +x script.sh

Luego, se debe especificar dentro del archivo, qué intérprete va a usarse en el subshell. Para ello la primera línea del fichero, debe tener el shebang que es #!, de la siguiente manera:

#! /bin/bash -e

Al final en realidad se puede colocar cuaqluier opción que acepte bash. En este caso -e significa exit on error lo cual quiere decir que en caso de presentarse un error el script se va a interrumpir. Otra opción común es -x el cual sirve para fines de debug, imprimiendo las líneas mientras se va ejecutando.

Se pueden agregar comentarios dentro de un script mediante el símbolo de numeral #. Los comentarios son útiles para fines de docuemntación con la finalidad de dar instrucciones acerca de lo que hace el fichero o indicaciones para quien lo ejecute acerca de sus opciones.

Un ejemplo de script en bash puede ser el siguiente:

Script de ejemplo script_user.sh
#! /bin/bash
# Este es un fichero de ejemplo que imprime el usuario y directorio actual

echo "Script de inicio"
echo "Bienvenido $USER!"
echo "Nos encontramos en $PWD"

Para ejecutar este fichero se debe realizar lo siguiente:

$ chmod +x script_user.sh
$ ./script-user.sh

1.3. Material para revisar

1.4. Referencias

1.5. Actividades

1.5.1. Preparando entorno de ejecución

Antes de comenzar a programar en bash es necesario contar con una terminal que cuente con este intérprete de shell y con un editor para escribir los scripts.

1.5.1.1. Obtener Linux

Como se mencionó en el texto anterior, Bash ya viene por defecto en los sistemas Linux al ser su intérprete por defecto. No obstante, también es posible hallarlo en sistemas MacOS.

Respecto a la distribución de Linux para emplear, en realidad no hay mucha diferencia en cuanto se va a trabajar con bash. No obstante, por mayor practicidad se puede optar por Ubuntu 20.04 o 22.04. En caso de preferir explorar otras opciones, recomendaría emplear alguna con una buena cantidad de usuarios (soporte de la comunidad) y con una buena documentación. Por ejemplo, se puede utilizar Fedora, ArchLinux, Manjaro, OpenSuse, entre otros.

En caso de Windows, se tiene algunas opciones, puede escoger la que se ajuste más a sus necesidades.

  1. Utilizar el Windows Subsystem Linux (WSL). A partir de Windows 10 versión 2004, se puede instalar un subsistema de Linux en Windows, el cual provee entre otras cosas, un entorno con bash. En algunos sistemas puede ya encontarse instalado por defecto, de no ser así se puede instalar siguiendo los pasos de esta guía de Windows: https://learn.microsoft.com/es-es/windows/wsl/install

    Advertencia

    Advertencia: Si bien el WSL puede funcionar relaivamente bien, es posible que no se tenga todas las características de Linux, por lo que de ser posible explorar las otras alternativas.

  2. Instalar en una máquina virtual. Esta es una alternativa para contar con un sistema de Linux en Windows con completa funcionalidad, pero con un impacto relativo en el rendimiento. Requiere de un software para virtualización. Recomendaría emplear VirtualBox, dado que es sencillo para instalar y existen bastantes guías para crear una máquina virtual con él. Respecto a la distribución, se puede emplear alguna de las citadas anteriormente, con preferencia en Ubuntu. En Internet se pueden encontrar varias referencias de cómo instalarlo, una de ellas es esta: Install Ubuntu on Oracle VirtualBox

  3. Instalar en otra partición del disco duro (Dual Boot). Es la mejor alternativa para desarrollar en Linux. Si se cuenta con la paciencia y el tiempo disponible recomiendo esta opción, dado que tendrá Linux de forma nativa y se evitará varios de los inconvenientes ya sea por temas de compatibilidad con el WSL o por temas de rendimiento de una máquina virtual. No obstante, el proceso implica un riesgo de eliminación de información si no se hace de forma adecuada, por lo que si no se tiene mucha experiencia con ello es mejor primero hacer un backup de lo archivos de Windows antes de aventurarse con los demás pasos requeridos para contar con una instalación Dual Boot. Una guía para realizar este procedimiento es esta: How to Dual Boot Windows 10 and Ubuntu

  4. Utilizar un servidor remoto. Tambien se cuenta con un Linux nativo, no obstante, se prescinde de la interfaz gráfica, la cual puede ser de ayuda si no se está muy familiarizado con la consola. Otra desventaja es que los servicios en la nube usualmente implican un costo mensual, que, si no van a emplear para el despliegue de un servicio para que esté disponible en Internet, posiblemente no se la mejor alternativa. Existen varios proveedores de infraestructura en la nube, de los cuales el que he encontrado más accesible en cuanto a relación al precio es Linode. Para acceder a este servicio basta con crear una cuenta y desplegar una máquina virtual mínima, donde se instale Linux. Finalmente, una vez desplegado se puede conectar a esta instancia mediante SSH. Aquí hay una vídeo con una guía completa de introducción al ecosistema de Linode: Linode Getting Started Guide

    Nota

    Si se va a utilizar un editor con interfaz gráfica, es preferible emplear alguna de las otras opciones mostradas anteriormente.

1.5.1.2. Instalar editor para bash

Para editar scripts de bash básicamente se puede emplear cualquier editor de texto. Existe una amplia lista de editores para Linux, tal como se muestra en esta entrada: Text editors

En general, se pueden distiguir dos tipos de editores: los que pueden utilizarse desde la consola, y los que requiren de una interfaz gráfica.

  • Editores de consola. Algunos de los más conocidos son:

    • Nano Uno de los más simples que se encuentra por defecto en la mayoría de instalaciones. Tiene algunos atajos de teclado, pero su función es limitada a simplemente editar texto.

    • Vim/NeoVim Es una versión mejorada del editor vi heredado de los sistemas UNIX. Es un editor modal, lo cual quiere decir que tiene modos de uso (normal, insertar, visual). Es bastante configurable, en especial NeoVim, el cual puede completarse con diferentes plugins para crear toda una IDE (Entorno de Desarrollo Integrado).

    • Emacs Es un editor modal también creado por el proyecto GNU/Linux. También es bastante configurable y ampliable en su funcionalidad con una serie de extensions.

    De escoger alguno de los editores de console, recomendaría emplear NeoVim. Si bien es cierto tiene una curva de aprendizaje un poco alta, una vez familiarizado con el entorno, es bastante útil para programar. En Udemy hay un curso muy interesante sobre Vim: Vim, aumenta tu velocidad de desarrollo. Este es mi editor por defecto, por lo que, de estar interesado, puede emplear mi archivo de configuración en LuighiV/nvim-config.

  • Editores con interfaz gráfica Entre los más conocidos y usados se encuentra:

    • Visual Studio Code Es un editor bastante completo y versatil, con el cual se puede desarrollar prácticamente en cualquier lenguage de programación, y para un gran variedad de plataformas. Es un editor que multiplataforma, por lo que funciona en los tres sistemas operativos más usados (Windows, MacOs y Linux). Para instalarlo puede referirse a esta guía: Setting up Visual Studio Code

1.5.2. Sesión síncrona

Horario

2020-10-15, 6-8pm

Temas

Introducción a scripts, comandos en scripts, declaración de variables, uso de variables, estructuras condicionales, verificación de variables de entorno, uso de argumentos de scripts, creación de funciones, creación de arreglos, estructuras de control iterativas

1.5.2.1. Recursos

Este es el fichero utilizado en la sesión síncrona, que combina los diferentes temas tratados.

Script ejemplo.sh
#! /bin/bash
# Este es un comentario

if [[ ! -z "${ENV_TYPE}"  ]]; then
    echo "El entorno de ejecución es: ${ENV_TYPE}"
else
    ENV_TYPE=local
    echo "Entorno no definido. Por defecto local"
fi

FOLDER_NAME=$1
FILE_NAME=$2

echo "Inicio del script"
echo "Path del sistema: $PATH"
echo "Usuario: $USER"
echo "Ubicación: $PWD"

function create_folder(){
    mkdir -p ${1}
    cd ${1}

    if [[ -f "${2}" ]]; then
        echo "El archivo ${2} se encuentra en el directorio"
    else
        echo "El archivo NO se encuentra, creando nuevo archivo"
        touch ${2}
    fi

    echo "Comandos iniciales" > ${2}
    echo "$PATH" >> ${2}
    echo "Finalización del programa"
    cd ..
}

FOLDERS=($FOLDER_NAME ${FOLDER_NAME}_v1 ${FOLDER_NAME}_v2)

for folder in ${FOLDERS[@]}; do
    create_folder $folder $FILE_NAME
done

1.5.3. Ejercicios

A continuación se presentan una serie de ejercicios para practicar el scripting para bash. No existe manera única de desarrollarlo. El objetivo es poder aplicar algunos de los comandos y conceptos revisados en la teoría y en el material de ayuda.

  1. Crear un fichero de bash que imprima la información del sistema operativo, la ubicación actual del fichero y el usuario que lo ejecuta.

  2. Escribir un script que lea las variables de entorno DB_PASS y DB_USER y las escriba en un fichero .env ubicado en la misma carpeta desde donde se ejecuta el archivo.

  3. Escribir un fichero que procese un arreglo de strings y por cada string, imprima su valor y lo concatene en un único string separado por comas.