Mejores practicas para crear un Script de Shell en GNU/Linux

Por lo general, cuando uno empieza ha trabajar en el área de Administración de Servidores con Sistemas Operativos GNU/Linux y / o Unix, uno se encuentra (enfrenta) ha trabajar en un ambiente donde por lo general hay un montón de tareas programadas que otros administradores escribieron y que en algún momento deberemos gestionar (administrar) para resolver algún problema, mejorar y / o eliminar, para cumplir con algún nuevo requerimiento de la Institución donde trabaja. Así que no es extraño, que cualquier nuevo SysAdmin en cualquier lugar de trabajo, se enfrente a la engorrosa tarea de entender algunos de los Script de Shell creados por otros antiguos SysAdmin, que no están bien escrito, o están en una estructura lógica o de redacción, nada fácil de entender, o en el peor de los casos, con ordenes de comandos, atípicas, antiguas, ineficientes, o escritas de forma torpe y confusa.

Si bien la solución de los scripts mal escritos es siempre una molestia momentánea, esto le enseña a cualquier buen SysAdmin algo importante. Si uno va ha crear un Script de Shell que se va a utilizar más allá de hoy, siempre es mejor escribirlos de forma muy profesional y estandarizada, para que pasado el tiempo, cualquier otro, o uno mismo, pueda con el mínimo esfuerzo y conocimiento lograr una comprensión y administración en un mínimo de tiempo.

Por eso, luego de la serie practica de publicaciones sobre “Aprender Shell Scripting” donde examinamos algunos scripts muy prácticos con comandos sencillo y básicos, empezaremos con esta nueva serie llamada “Mejores practicas para crear un Script de Shell en GNU/Linux”, donde nos enfocaremos a fondo en cada pequeño aspecto de los mismo y el porque de muchas cosas, es decir, cubriremos algunos consejos que nos harán realizar mejores scripts, pero no tanto para nosotros mismo, sino para la siguiente persona (SysAdmin) que tenga que gestionarlos. Para que no tenga que pasar por una tediosa y difícil tarea de averiguar que codifico, como y porqué, y por qué ya no funciona.

En esta primera (1°) publicación de esta nueva serie “Mejores practicas para un buen Script de Shell para GNU/Linux” hablaremos sobre lo que va o debería ir en el encabezado del Script de Shell.

=======================================
ENCABEZADO – INVOCACIÓN DEL SHELL
=======================================

#!/ruta/interprete [argumento-parámetro]

La Linea superior es la estructura básica con la que se invoca un Shell Script para GNU/Linux. Sus elementos pueden describirse de la siguiente manera:

#! => sha-bang

El sha-bang (#!) en la parte superior del Script creado o ha crearse es una secuencia de comandos que le dice a nuestro Sistema Operativo que nuestro archivo es un conjunto de comandos que se alimentará (será interpretado) por el intérprete de comandos indicado luego del mismo. El par de caracteres #! en realidad, es un número mágico de dos bytes, un marcador especial que designa un tipo de archivo, y en nuestro caso, un script de shell ejecutable. Inmediatamente después del sha-bang viene el nombre de la ruta donde se ubica el interprete a ejecutarse más el nombre de dicho interprete. En otras palabras, esta es la ruta de acceso al programa que interpreta los comandos en el guión, ya se trate de una interprete, un lenguaje de programación, o una utilidad. Este intérprete de comandos a continuación, ejecuta los comandos en el script, comenzando en la parte superior (la línea que sigue a la del sha-bang), y haciendo caso omiso de los comentarios. Algunos sha-bang pueden ser:

#!/bin/sh
#!/bin/bash
#!/usr/bin/perl
#!/usr/bin/tcl
#!/bin/sed -f
#!/usr/awk -f

Cada una de las líneas arriba descritas (como ejemplo) invoca un intérprete de comandos diferentes. La linea /bin/sh, invoca al shell por defecto (Bash en un Sistema Operativo GNU/Linux) u otro similar. Usando #!/bin/sh, el valor predeterminado del Bourne Shell en la mayoría de las variantes comerciales de los Sistemas Operativos basados en UNIX, hace que el Script creado sea portable hacia otros Sistemas Operativos que no son Linux propiamente, sino similares o basados en él o UNIX, aunque esto sacrifica características específicas de BASH. Sin embargo, la secuencia “#!/bin/sh” se ajusta a la norma POSIX sh estándar.

Tenga en cuenta que la ruta dada en el sha-bang debe ser correcta, de lo contrario un mensaje de error, por lo general, “Comando no encontrado”, será el único resultado de la ejecución del script. Recuerde que el par de caracteres ” #! “ se puede omitir si el Script se compone sólo de un conjunto de comandos genéricos del Sistema Operativo, es decir, sin utilizar directivas internas del Shell. Y tenga en cuenta una vez más que ” #!/bin/sh “ invoca el intérprete shell por defecto, que por defecto es ” #!/bin/bash “ en un equipo con el Sistema Operativo GNU/Linux.

Con respecto a los argumentos, son varios los que se pudiesen emplear pero el más común es: ” -e “. el cual hace que el script valide los errores de ejecución de algún comando (linea de ejecución) y en caso positivo, forza la parada y salida del mismo, uno típico es ” -f “ para indicar cuál es el script que tiene que cargar y uno de los más raros es ” -rm “ que realiza el borrado del mismo una vez finaliza su ejecución. Solo es posible especificar en el sha-bang hasta un único argumento (parámetro) después del nombre del programa a ejecutar.

Y por ultimo, indicarle al script las variables globales que utilizará en partes esenciales del código, para validación de eventos, tales como la ruta de ejecución, usuario autorizado, nombre del script, entre otros. Y finalizar con los datos del programa, creador, organización, entre otros, más el licenciamiento que aplica al programa.

Mi consejo (Mejores practicas) para escoger el mejor sha-bang y encabezado de un Script de Shell son:

#!/usr/bin/env bash

Porqué mediante el comando ” env “ le indicamos al Sistema Operativo el interpreté a usarse con la ruta exacta especificada dentro del mismo por defecto, lo cual nos permite tener un sha-bang que nos aumente la portabilidad del mismo, debido a que no en todos los S.O. GNU/Linux los interpretes o programas tienen la misma ruta. Y sin argumentos, debido a que para eso es mejor el uso del comando set, debido a que con el podemos validar errores, generales (-e) o específicos (+x / -x), o para limpiar los valores predefinidos globales para las variables de entornos (-i) o especificas (-u / –unset). Y por ultimo, para ejecutar acciones determinadas (- o) complementarias dentro del script.

Por lo que mi ENCABEZADO recomendado sería:

#!/usr/bin/env bash
# Indicar el interprete bash con ruta absoluta por Sistema Operativo.

set -o errexit
# Para indicarle al script detener y cerrarse cuando un comando o linea de ejecución falle.

set -o nounset
# Para indicarle al script detener y cerrarse cuando la secuencia de comandos intenta utilizar variables no declaradas.

set -o pipefail
# Para obtener el estado de salida de la última orden que arrojó un código de salida distinto de cero.

# set -o xtrace
# Para rastrear lo que se ejecuta. Útil para la depuración. Habilítelo para verificar errores solamente.

Recuerde adicionalmente seguir estas recomendaciones:

01.- Indente su código: La fabricación de su código en forma legible es muy importante, y es algo que mucha gente parece olvidar también. Procure realizar las indentaciones necesarias para percibir una buena estructura lógica a la vista.

02.- Añada espacios de separación entre secciones de código: Esto puede ayudar a que el código sea mucho más entendible, ya que el espaciamiento por módulos o secciones ayuda ha hacer el código legible y fácil de entender.

03.- Comente lo más que pueda el código: Arriba (o abajo) de cada Orden de Comando (Linea de Ejecución) o Sección de Código es ideal añadir una descripción de la función de la(s) secuencia(s) de comandos para explicar lo que pasa dentro del propio código.

04.- Crear variables con nombres descriptivos de sus funciones: Asigne nombres de variables descriptivos que identifiquen de forma obvia la función para la cual será creada. Aunque cree variables temporales que nunca se utilizaran fuera de un bloque de código único, incluso así es bueno poner un nombre que implícitamente (objetivamente) de explicación de qué valores o funciones maneja.

05.- Utilice la sintaxis VARIABLE=$(comando) para la sustitución de comandos: Si desea crear una variable cuyo valor se derive de otro comando, hay dos maneras de hacerlo en bash. Con backtick, es decir, con los caracteres ` ` , Ejm: VARIABLE=`comando -opciones parámetros`, pero ya esta en desuso, así que la sintaxis VARIABLE=$(comando) es la manera más moderna, aceptada y recomendada. NO –> DATE=`date +%F` / SI –> DATE=$(date +%F)

06.- Utilice módulos y / o variables de Validación de Superusuario y Usuario Autorizado con o sin contraseña: Para aumentar los niveles de seguridad en caso de ser necesario.

07.- Utilice módulos y / o variables de Validación del Sistema Operativo (Distro, Versión, Arquitectura): para prevenir uso en plataformas no adecuadas.

08.- Utilice módulos (procedimientos / secciones) de confirmación de ejecución de acciones (módulos / funciones) criticas o por lotes: Para minimizar errores por improvisación o descuido.

09.- Provea Interfaces Amigables al usuario (User-friendly): Por Terminal con menús y colores con dialog y con Interfaces gráficas para Usuarios básicos con Zenity, Gxmessage. Y si es posible use el apoyo de alertas sónicas identificadoras de eventos reconocibles según el sonido. Traté en la medida de lo posible que su Script pueda funcionar de las 2 maneras con solo habilitar y deshabilitar opciones / módulos / funciones.

10.- Inclúyale módulos (mensajes) de Bienvenida y Despedida: en caso de ser necesarios para aumentar la interactividad con el usuario.

11.- Incluya un modulo de verificación de doble ejecución: Créele un archivo de bloqueo para evitar que pueda ser ejecutado mas de 1 vez al mismo tiempo.

12.- Racionalice el tamaño del script con Funciones y / o Módulos externos: Si el Script es muy grande divida el código utilizando funciones o divídalos en pequeños script que sean invocados por medio de uno principal.

13.- Invocación de forma clara y evidente los llamados a otros Interpretes (lenguajes de programación) dentro del Script: Invóquelos de forma clara por lineas o módulos.

Ejemplo:

# ================================================== #
#!/bin/bash
#Llamando a un interprete externo a BASH
echo 'El siguiente texto será mostrado por el interprete de PERL'
perl -e 'print "Este texto es mostrado por un script PERL embebido.\n";'
exit 0
# ==================================================#

 

# ==================================================# 
#!/bin/bash #Llamando al interprete de Python. 
echo 'El siguiente es un script de python:'
echo print "Hola, mundo!" | tee $HOME/.testpythonbash.py
python $HOME/.testpythonbash.py exit 0
# ==================================================#

 


# ======================================================= #
#!/bin/bash
# bash-y-perl.sh

echo "Saludos desde la parte BASH del script."
# Es posible añadir mas comandos BASH aqui.

exit 0
# Fin de la parte BASH del script.

###########################################################

#!/usr/bin/perl
# Esta parte del script se invoca con la opcion -x.

print "Saludos desde la parte PERL del script.\n";
# Podemos añadir mas comandos PERL aqui.

# Fin de la parte PERL del script.
# ======================================================= #
 

En próximas publicaciones ampliaremos más a detalles cada uno de las practicas arriba descritas.

Y si conoces algunas otras buenas practicas propias u ajenas, no dudes en comentarlas para hacer un compendio mas completo!

Hasta la siguiente publicación de esta nueva serie.


6 comentarios

  1.   Max J Rodriguez dijo

    Un detalle nada mas, es “shebang” 😛
    muy buen post, las buenas prácticas a la larga siempre ayudan a estandarizar.

  2.   Uno Que Pasaba Por Aqui dijo

    Bash no es la shell por defecto en todas las distribuciones, y por lo tanto el enlace simbolico /bin/sh no apunta siempre a bash. En Debian por ejemplo (y supongo que por lo tanto Ubuntu):
    $ ls -l /bin/sh
    lrwxrwxrwx 1 root root 4 aza 8 2014 /bin/sh -> dash
    La shell por defecto por tanto en Debian es dash. Ver aqui: https://wiki.debian.org/Shell

  3.   nameless dijo

    Como tip para saber la Shell en Uso:

    echo $0
    echo $SHELL
    env | grep SHELL

  4.   Ing. Jose Albert dijo

    Efectivamente tienes razón! Probe en DEBIAN 9 y Kali Linux 2.0 y es cierto! te lleva a dash. Con más razón aún la recomendación de: #!/usr/bin/env bash si es el Shell que se quiere usar.

    Y tienes toda la razón es shebang, pero en algunos sitios web (literaturas técnicas) le llaman shabang u otras palabras, de ahí mi confusión. Ejemplo:

    In computing, a shebang is the character sequence consisting of the characters number sign and exclamation mark (#!) at the beginning of a script. It is also called sha-bang,[1][2] hashbang,[3][4] pound-bang,[5] or hash-pling

    De: https://en.wikipedia.org/wiki/Shebang_%28Unix%29

    Y Chapter 2. Starting Off With a Sha-Bang
    De: http://www.tldp.org/LDP/abs/html/sha-bang.html

  5.   Ing. Jose Albert dijo

    También:

    http://unix.stackexchange.com/questions/50425/shell-scripts-are-still-working-without-sha-bang-line

    https://mohammednv.wordpress.com/2013/05/30/sha-bang-line-in-scripts/

  6.   Ing. Jose Albert dijo

    También: basename $0

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

*

*

  1. Responsable de los datos: Miguel Ángel Gatón
  2. Finalidad de los datos: Controlar el SPAM, gestión de comentarios.
  3. Legitimación: Tu consentimiento
  4. Comunicación de los datos: No se comunicarán los datos a terceros salvo por obligación legal.
  5. Almacenamiento de los datos: Base de datos alojada en Occentus Networks (UE)
  6. Derechos: En cualquier momento puedes limitar, recuperar y borrar tu información.