Automatización con Shell Scripts
Tags: spanish, shell, automate, todayilearned
Published: 2019-02-23 05:00:45

---
title: Automatización con Shell Scripts
published: true
description: Aprendí a usar Shell Scripting y en este tutorial comparto las cosas que aprendí con un script simple de instalación de un proyecto.
tags: spanish, shell, automate, til
canonical_url: https://sergiodxa.com/articles/automatizacion-shell-scripts
---

_Publicado originalmente en https://sdx.im/articles/automatizacion-shell-scripts_

Hace unos días tuve la necesidad de crear un script para automatizar el proceso de instalación de un proyecto y me tocó aprender Shell Scripting. Acá las cosas que aprendía creándolo.

> _Nota_: Esto está basado en un script que tuve que hacer para un proyecto en el que estoy trabajando.

## Creando el archivo

Lo primero es crear el archivo, vamos a llamarlo `setup.sh`. Lo podemos colocar en cualquier carpeta, por ejemplo en `~/` haciendo `touch ~/setup.sh`.

## Condiciones

Luego vamos a ir agregando el código. Vamos a hacer que nuestro script tenga dos funcionalidades, `install` y `run`, para esto necesitamos una simple condición.

```shell
if [ "$1" = "install" ]
then
  # Install the app
elif [ "$1" = "run" ]
then
  # Run the app
else
  # Throw an error
fi
```

Si vemos estamos usando algo llamado `$1`, eso es una variable, en este caso hacer referencia al primer argumento que le pasemos a nuestro script al ejecutarlo. Por ejemplo, si para ejecutarlo hacemos esto:

```bash
bash ~/setup.sh install
```

Entonces `install` es el valor de `$1`.

Lo siguiente que vemos es como hacen las condiciones, la sintaxis es:

```shell
if [ condición ]
then
  # algo
elif [ otra condición]
then
  # otra cosa
else
  # algo más
fi
```

La condición se inicia con `if`. Después del `if` va la primer condición entre corchetes y en la siguiente línea un `then`, luego de este va el código a ejecutar si se pasa la condición, para hacer un `else if` se usa la palabra clave`elif` seguida por la condición entre corchetes y el `then`, igual que un `if` normal. Para el último caso hacemos `else` sin necesidad de poner un `then`. Por último ponemos un `fi` que sirve para terminar el bloque de condiciones.

## Funciones

Vamos a crear algunas funciones para ordenar nuestro código. Creemos una función que nos permita mostrarle texto al usuario de distintos colores dependiendo de que ocurrió, vamos a tener entonces `throw`, `warn` y `print` que va a mostrar texto en rojo, amarillo y verde, respectivamente.

```shell
NC='\033[0m'

throw()
{
  COLOR='\033[0;31m'
  >&2 echo -e ${COLOR}$1${NC}
}

print()
{
  COLOR='\033[0;32m'
  echo -e ${COLOR}$1${NC}
}

warn()
{
  COLOR='\033[0;33m'
  echo -e ${COLOR}$1${NC}
}
```

La forma de definir una función es simplemente poner el nombre de la función seguida por `()`. Luego creamos un bloque de código usando llaves y dentro va el código de la función. En nuestro caso son funciones más o menos similares, en todas creamos una variable llamada `COLOR` con su valor, `'\033[0;31m'` significa rojo, `'\033[0;32m'` significa verde y `'\033[0;33m'` significa amarillo, la variable `NC` que creamos antes de las funciones significa _No Color_ y la vamos a usar para hacer que el texto deje de tener color.

Lo siguiente que hacemos es hacer un `echo` para mostrar texto en pantalla, le pasamos el flag `-e` para soportar `\`, esto es necesario para usar colores, y le pasamos como valor a mostrar en pantalla `${COLOR}$1${NC}` ¿Qué significa esto? Primero ponemos el contenido de nuestra variable de color, después colocamos `$1` que, si recordás, es el primer argumento que le pasamos a nuestro script, resulta que dentro de una función es el primer argumento que le pasemos a la función, en nuestro caso el texto, y por último ponemos `${NC}` que que no tenga color otra vez y no se quede el resto del texto en rojo, verde o amarillo.

Hay un caso diferente al resto que es la función `throw`, antes del `echo` se agrega `>&2`. Eso significa que el contenido del `echo` lo debe pasar a `stderr` en vez de `stdout` (lo normal). Esto es para que si algún programa usa nuestro script va a poder identificar los `throw` como errores correctamente.

Ahora podríamos empezar a usar estas funciones, por ejemplo agreguemos que en nuestro `else` se muestre un mensaje de error en rojo.

```shell
NC='\033[0m'

throw()
{
  COLOR='\033[0;31m'
  >&2 echo -e ${COLOR}$1${NC}
}

print()
{
  COLOR='\033[0;32m'
  echo -e ${COLOR}$1${NC}
}

warn()
{
  COLOR='\033[0;33m'
  echo -e ${COLOR}$1${NC}
}

if [ "$1" = "install" ]
then
  # Install the app
elif [ "$1" = "run" ]
then
  # Run the app
else
throw "Invalid command, available values are 'install' or 'run'"
fi
```

Con esto ya vamos avanzando. Ahora hagamos la instalación.

## Función de instalación

Creemos una función llamada `install` que nos va a instalar nuestra aplicación. El proceso para instalar debe cubrir todo, la idea es que podamos correr este script en una computadora nueva y nos deje el entorno listo.

Para esto vamos a necesitar realizar varios pasos:

- Instalar Homebrew
- Instalar Git
- Crear una clave SSH
- Agregarla a GitHub
- Instalar Yarn y Node.js
- Clonar desde GitHub nuestro repositorio
- Instalar dependencias

En código esto sería algo así

```shell
confirm_ssh()
{
  read -p "Have you added the SSH Key? [y/N] " CONFIRM_SSH
  if [[ $CONFIRM_SSH -ne "y" || $CONFIRM_SSH -ne "Y" ]]
  then
    warn "Please add it to continue."
    confirm_ssh
  fi
}

install()
{
  /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
  print "Homebrew installed"

  brew install git
  print "Git installed"

  warn "Please, enter your GitHub email address and name"
  read -p "Email address: " EMAIL
  read -p "Name: " NAME
  git config --global user.name "$NAME"
  git config --global user.email "$EMAIL"
  echo "Generating SSH Key"
  ssh-keygen -t rsa -b 4096 -C "$EMAIL"
  eval "$(ssh-agent -s)"
  ssh-add -K ~/.ssh/id_rsa
  pbcopy < ~/.ssh/id_rsa.pub
  warn "We have copied your new SSH Key to your clipboard, please add it to your GitHub account going to https://github.com/settings/keys"
  sleep 2.5
  open "https://github.com/settings/keys"
  confirm_ssh
  print "Git & GitHub configured"

  brew install yarn
  print "Yarn & Node.js installed"

  git clone git@github.com:sergiodxa/personal-site.git ~/website
  print "Repository clonned"

  cd ~/website && npm install ; cd -
  print "Dependencies installed"

  print "Project succesfully installed, you could run it with 'bash ~/setup.sh run'"
}
```

Veamos que hacen estas funciones `install` y `confirm_ssh`.

Empecemos por `install`, lo primero que hacemos es instalar Homebrew, al terminar mostramos un mensaje diciendo que fue instalado. Después usamo Homebrew para instalar Git, otra vez le avisamos al usuario que fue instalado.

Le advertimos al usuario que vamos a necesitar su nombre y dirección de email y usamos `read` para pedirle que ingrese estos valores, el flag `-p` nos permite pasar un texto para mostrarse a la izquierda de donde el usuario va a ingresar su nombre y email, por último a `read` le pasamos el nombre de la variable donde queremos guardar lo que el usuario escriba.

Una vez tenemos `$NAME` y `$EMAIL` procedemos a configurar Git para que use estos valores y generamos una nueva clave SSH cuyo valor copiamos al portapapeles usando `pbcopy < ~/.ssh/id_rsa.pub`. Le advertimos al usuario que copiamos su clave SSH al portapapeles y que tiene que agregarla a su cuenta de GitHub, esperamos dos segundos y medio y abrimos en el navegador la URL donde se agrega la clave SSH, esta espera es para dar tiempo a leer.

Después de esto llamamos a la función `confirm_ssh`. Esta función muy simple usa `read` como ya vimos para preguntarle al usuario si ya agregó la clave SSH, si el usuario no escribe `y` o `Y` entonces se muestra el mensaje la advertencia "Please add it to continue" y se vuelve a llamar a la función `confirm_shh` recursivamente, de esta forma mientras el usuario no escriba `y` o `Y` no va a pasar de `confirm_ssh`.

Luego de todo esto instalamos Yarn usando Homebrew y este además instala Node.js, lo que nos da un dos por uno. Clonamos el repositorio de nuestro proyecto a una carpeta ya conocida, también podríamos usar `read` para preguntarle al usuario a que carpeta clonar el repositorio.

Después de esto ejecutamos `cd ~/website && yarn ; cd -`, esto lo que hace es movernos a la carpeta donde clonamos nuestro proyecto, ejecutar `yarn` y al terminar de instalar las dependencias volver a la carpeta donde estábamos originalmente.

Por último se muestra un mensaje indicando como ejecutar el proyecto.

## Función de ejecución

Ahora que ya instalamos todo, vamos a crear la función para iniciar nuestro proyecto.

```shell
run()
{
  print "Running project"
  cd ~/website && yarn dev ; cd -
}
```

Eso es todo, mostramos un mensaje diciendo que vamos a correr el proyecto, nos movemos a la carpeta donde clonamos el repositorio y ejecutamos `yarn dev` para iniciarlo, este script `dev` lo uso en mi sitio personal para iniciarlo en modo desarrollo. Al terminar va a volver a la carpeta inicial.

## Poniendo todo junto

Ahora juntemos todo, de forma ordenada y ejecutemos nuestras nuevas funciones en las condiciones correspondientes.

```shell
NC='\033[0m'

throw()
{
  COLOR='\033[0;31m'
  >&2 echo -e ${COLOR}$1${NC}
}

print()
{
  COLOR='\033[0;32m'
  echo -e ${COLOR}$1${NC}
}

warn()
{
  COLOR='\033[0;33m'
  echo -e ${COLOR}$1${NC}
}

run()
{
  print "Running project"
  cd ~/website && yarn dev ; cd -
}

confirm_ssh()
{
  read -p "Have you added the SSH Key? [y/N] " CONFIRM_SSH
  if [[ $CONFIRM_SSH -ne "y" || $CONFIRM_SSH -ne "Y" ]]
  then
    warn "Please add it to continue."
    confirm_ssh
  fi
}

install()
{
  /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
  print "Homebrew installed"

  brew install git
  print "Git installed"

  warn "Please, enter your GitHub email address and name"
  read -p "Email address: " EMAIL
  read -p "Name: " NAME
  git config --global user.name "$NAME"
  git config --global user.email "$EMAIL"
  echo "Generating SSH Key"
  ssh-keygen -t rsa -b 4096 -C "$EMAIL"
  eval "$(ssh-agent -s)"
  ssh-add -K ~/.ssh/id_rsa
  pbcopy < ~/.ssh/id_rsa.pub
  warn "We have copied your new SSH Key to your clipboard, please add it to your GitHub account going to https://github.com/settings/keys"
  sleep 2.5
  open "https://github.com/settings/keys"
  confirm_ssh
  print "Git & GitHub configured"

  brew install yarn
  print "Yarn & Node.js installed"

  git clone git@github.com:sergiodxa/personal-site.git ~/website
  print "Repository clonned"

  cd ~/website && npm install ; cd -
  print "Dependencies installed"

  print "Project succesfully installed, you could run it with 'bash ~/setup.sh run'"
}

if [ "$1" = "install" ]
then
  install
elif [ "$1" = "run" ]
then
  run
else
  throw "Invalid command, available values are 'install' or 'run'"
fi
```

Eso es todo, si copias el contenido de este archivo a `~/setup.sh` y lo ejecutamos con `bash ~/setup.sh install` o `bash ~/setup.sh run` vamos a tener todo listo.

Adicionalmente podríamos llamar la función `run` al final de la instalación para ya tener todo listo.

## Palabras finales

Al principio usar Shell Scripting fue algo raro ya que nunca había tenido la necesidad de usarlo antes, pero es bastante simple y divertido y estoy pensando que otras cosas se podrían automatizar mediante scripts de Shell para hacer tareas de forma más sencilla.