Cómo usar set y pipefail en Bash Scripts en Linux

Terminal Linux en la pantalla de un portátil sobre un fondo azul.
fatmawati achmad zaenuri/Shutterstock.com

Los comandos de Linux sety pipefaildictan lo que sucede cuando ocurre una falla en un script de Bash . Hay más en qué pensar que si debe detenerse o continuar.

Bash Scripts y condiciones de error

Los scripts de Bash Shell son geniales. Son rápidos de escribir y no necesitan compilación. Cualquier acción repetitiva o de varias etapas que necesite realizar puede incluirse en un guión conveniente. Y debido a que los scripts pueden llamar a cualquiera de las utilidades estándar de Linux, no está limitado a las capacidades del propio lenguaje de shell.

Pero pueden surgir problemas cuando llama a una utilidad o programa externo. Si falla, la utilidad externa se cerrará y enviará un código de retorno al shell, e incluso podría imprimir un mensaje de error en la terminal. Pero su secuencia de comandos continuará procesando. Tal vez eso no es lo que querías. Si se produce un error al principio de la ejecución de la secuencia de comandos, podría generar problemas peores si se permite que se ejecute el resto de la secuencia de comandos.

Puede verificar el código de retorno de cada proceso externo a medida que se completan, pero eso se vuelve difícil cuando los procesos se canalizan a otros procesos. El código de retorno será del proceso al final de la canalización, no el del medio que falló. Por supuesto, también pueden ocurrir errores dentro de su secuencia de comandos, como intentar acceder a una variable no inicializada .

Los comandos sety pipefilele permiten decidir qué sucede cuando ocurren errores como estos. También le permiten detectar errores incluso cuando ocurren en medio de una cadena de tuberías.

Aquí se explica cómo usarlos.

Demostración del problema

Aquí hay un script Bash trivial. Hace eco de dos líneas de texto en la terminal. Puede ejecutar este script si copia el texto en un editor y lo guarda como «script-1.sh».

#!/bin/bash

echo Esto sucederá primero
echo Esto sucederá segundo

Para hacerlo ejecutable necesitarás usarchmod :

chmod +x script-1.sh

Deberá ejecutar ese comando en cada script si desea ejecutarlos en su computadora. Ejecutemos el script:

./script-1.sh

Ejecutar un script simple sin errores.

Las dos líneas de texto se envían a la ventana del terminal como se esperaba.

Relacionado:  ¿Qué es un DEX? Intercambios descentralizados explicados

Modifiquemos un poco el script. Le pediremos lsque enumere los detalles de un archivo que no existe. Esto fallará. Guardamos esto como «script-2.sh» y lo hicimos ejecutable.

#!/bin/bash

echo Esto sucederá primero
ls nombre de archivo imaginario
echo Esto sucederá segundo

Cuando ejecutamos este script, vemos el mensaje de error de ls.

./script-2.sh

Ejecutar un script y generar una condición de error.

Aunque el lscomando falló, el script continuó ejecutándose. Y aunque hubo un error durante la ejecución del script, el código de retorno del script al shell es cero, lo que indica éxito. Podemos verificar esto usando echo y la $?variable que contiene el último código de retorno enviado al shell.

eco $?

Comprobación del código de retorno del último script ejecutado.

El cero que se informa es el código de retorno del segundo eco en el script. Así que hay dos problemas con este escenario. La primera es que el script tuvo un error pero siguió ejecutándose. Eso puede generar otros problemas si el resto del script espera o depende de que la acción que falló realmente tuvo éxito. Y la segunda es que si otro script o proceso necesita comprobar el éxito o el fracaso de este script, obtendrá una lectura falsa.

La opción set -e

La set -eopción (salir) hace que una secuencia de comandos se cierre si alguno de los procesos a los que llama genera un código de retorno distinto de cero. Todo lo que no sea cero se considera un fracaso.

Al agregar la set -eopción al inicio del script, podemos cambiar su comportamiento. Esto es «script-3.sh».

#!/bin/bash
conjunto -e

echo Esto sucederá primero
ls nombre de archivo imaginario
echo Esto sucederá segundo

Si ejecutamos este script, veremos el efecto de set -e.

./script-3.sh
eco $?

Terminar un script en una condición de error y configurar correctamente el código de retorno.

El script se detiene y el código de retorno enviado al shell es un valor distinto de cero.

Lidiando con Fallas en Tuberías

Las tuberías añaden más complejidad al problema. El código de retorno que sale de una secuencia canalizada de comandos es el código de retorno del último comando de la cadena. Si hay una falla con un comando en el medio de la cadena, volvemos al punto de partida. Ese código de retorno se pierde y el script seguirá procesándose.

Relacionado:  ¿Qué significa "TIFU" y cómo lo uso?

Podemos ver los efectos de los comandos de canalización con diferentes códigos de retorno utilizando los integrados truey falseshell. Estos dos comandos no hacen más que generar un código de retorno de cero o uno, respectivamente.

verdadero
eco $?
falso
eco $?

Los comandos incorporados true y false de bash shell.

Si nos conectamos falsea true—con falsela representación de un proceso fallido— obtenemos trueel código de retorno de cero.

falso | verdadero
eco $?

Convertir lo falso en verdadero.

Bash tiene una variable de matriz llamada PIPESTATUS, y captura todos los códigos de retorno de cada programa en la cadena de tuberías.

falso | cierto | falso | verdadero
echo "${PIPESTATUS[0]} ${PIPESTATUS[1]} ${PIPESTATUS[2]} ${PIPESTATUS[3]}"

Uso de PIPESTATUS para ver el código de retorno de todos los programas en una cadena de tuberías.

PIPESTATUSsolo retiene los códigos de retorno hasta que se ejecuta el siguiente programa, y ​​tratar de determinar qué código de retorno va con qué programa puede volverse muy complicado muy rápidamente.

Aquí es donde entran set -o(opciones) y pipefail. Esto es «script-4.sh». Esto intentará canalizar el contenido de un archivo que no existe en wc.

#!/bin/bash
conjunto -e

echo Esto sucederá primero
gato script-99.sh | wc-l
echo Esto sucederá segundo

Esto falla, como era de esperar.

./script-4.sh
eco $?

Ejecutando un script con un error en una cadena de tuberías.

El primer cero es la salida de wc, que nos dice que no leyó ninguna línea del archivo que falta. El segundo cero es el código de retorno del segundo echocomando.

Agregaremos -o pipefailel archivo , lo guardaremos como «script-5.sh» y lo haremos ejecutable.

#!/bin/bash
establecer -eo pipefail

echo Esto sucederá primero
gato script-99.sh | wc-l
echo Esto sucederá segundo

Ejecutemos eso y verifiquemos el código de retorno.

./script-5.sh
eco $?

Ejecutar un script que atrapa errores en cadenas de tuberías y establece correctamente el código de retorno.

El script se detiene y el segundo echocomando no se ejecuta. El código de retorno enviado al shell es uno, lo que indica correctamente un error.

Captura de variables no inicializadas

Las variables no inicializadas pueden ser difíciles de detectar en un script del mundo real. Si intentamos con echoel valor de una variable no inicializada, echosimplemente imprime una línea en blanco. No lanza un mensaje de error. El resto del script seguirá ejecutándose.

Este es script-6.sh.

#!/bin/bash
establecer -eo pipefail

echo "$no establecido"
echo "Otro comando de eco"

Lo ejecutaremos y observaremos su comportamiento.

./script-6.sh
eco $?

Ejecutar un script que no captura variables no inicializadas.

El script pasa por encima de la variable no inicializada y continúa ejecutándose. El código de retorno es cero. Tratar de encontrar un error como este en un script muy largo y complicado puede ser muy difícil.

Relacionado:  Los mejores accesorios de Oculus Quest 2 de 2021

Podemos atrapar este tipo de error usando la set -uopción (desarmado). Lo agregaremos a nuestra creciente colección de opciones de configuración en la parte superior del script, lo guardaremos como «script-7.sh» y lo haremos ejecutable.

#!/bin/bash

establecer -eou fallo de tubería

echo "$no establecido"

echo "Otro comando de eco"

Ejecutemos el script:

./script-7.sh
eco $?

Ejecutar un script que captura variables no inicializadas.

Se detecta la variable no inicializada, el script se detiene y el código de retorno se establece en uno.

La -uopción (no establecida) es lo suficientemente inteligente como para no ser activada por situaciones en las que puede interactuar legítimamente con una variable no inicializada.

En “script-8.sh”, el script verifica si la variable New_Varestá inicializada o no. No desea que el guión se detenga aquí, en un guión del mundo real realizará más procesamiento y se ocupará de la situación usted mismo.

Tenga en cuenta que hemos agregado la -uopción como la segunda opción en la declaración de conjunto. La -o pipefailopción debe ser la última.

#!/bin/bash

establecer -euo pipefail

if [ -z "${Nueva_Var:-}" ]; entonces

echo "New_Var no tiene ningún valor asignado".

fi

En «script-9.sh», se prueba la variable no inicializada y, si no lo está, se proporciona un valor predeterminado en su lugar.

#!/bin/bash
establecer -euo pipefail

valor_predeterminado=484
Valor=${Nueva_Var:-$valor_predeterminado}
echo "Nueva_Var=$Valor"

Los scripts pueden ejecutarse hasta su finalización.

./script-8.sh
./script-9.sh

Ejecutar dos scripts donde las variables no inicializadas se manejan internamente y la opción -u no se activa.

Sellado con hacha

Otra opción útil para usar es la opción set -x(ejecutar e imprimir). Cuando estás escribiendo guiones, esto puede ser un salvavidas. imprime los comandos y sus parámetros a medida que se ejecutan.

Le brinda una forma rápida de seguimiento de ejecución «aproximada y lista». Aislar fallas lógicas y detectar errores se vuelve mucho, mucho más fácil.

Agregaremos la opción set -x a «script-8.sh», la guardaremos como «script-10.sh» y la haremos ejecutable.

#!/bin/bash
conjunto -euxo pipefail

if [ -z "${Nueva_Var:-}" ]; entonces
  echo "New_Var no tiene ningún valor asignado".
fi

Ejecútelo para ver las líneas de seguimiento.

./script-10.sh

Ejecutar un script con líneas de seguimiento -x escritas en el terminal.

Detectar errores en estos scripts de ejemplo triviales es fácil. Cuando comience a escribir guiones más complicados, estas opciones demostrarán su valor.