Manejo de errores y mejores prácticas para funciones
En esta última sección, exploraremos técnicas de manejo de errores y mejores prácticas para las funciones en Bash. Un manejo adecuado de errores es crucial para crear scripts robustos y mantenibles.
Creando un script con manejo de errores
Vamos a crear un nuevo script que demuestre un manejo robusto de errores:
cd ~/project/bash_functions
touch error_handling.sh
Agrega el siguiente contenido:
#!/bin/bash
## Enable error handling
set -e ## Exit immediately if a command exits with non-zero status
## Define a function to log messages
log_message() {
local level="$1"
local message="$2"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $message"
}
## Function to validate a number is positive
validate_positive() {
local num="$1"
local name="$2"
## Check if the argument is a number
if ! [[ "$num" =~ ^[0-9]+$ ]]; then
log_message "ERROR" "$name must be a number"
return 1
fi
## Check if the number is positive
if [ "$num" -le 0 ]; then
log_message "ERROR" "$name must be positive"
return 2
fi
return 0
}
## Function that divides two numbers
divide() {
local numerator="$1"
local denominator="$2"
## Validate inputs
validate_positive "$numerator" "Numerator" || return $?
validate_positive "$denominator" "Denominator" || return $?
## Check for division by zero
if [ "$denominator" -eq 0 ]; then
log_message "ERROR" "Division by zero is not allowed"
return 3
fi
## Perform division
local result=$(echo "scale=2; $numerator / $denominator" | bc)
echo "$result"
return 0
}
## Function to safely get user input
get_number() {
local prompt="$1"
local input
while true; do
read -p "$prompt: " input
if validate_positive "$input" "Input"; then
echo "$input"
return 0
else
log_message "WARN" "Invalid input. Please try again."
fi
done
}
## Disable automatic exit on error for the main script
set +e
## Main script logic
log_message "INFO" "Starting division calculator"
## Test with valid values
result=$(divide 10 2)
exit_code=$?
if [ $exit_code -eq 0 ]; then
log_message "INFO" "10 / 2 = $result"
else
log_message "ERROR" "Division failed with code $exit_code"
fi
## Test with invalid values
echo ""
log_message "INFO" "Testing with invalid values"
divide 0 5
log_message "INFO" "Exit code: $?"
divide 10 0
log_message "INFO" "Exit code: $?"
divide abc 5
log_message "INFO" "Exit code: $?"
## Interactive mode
echo ""
log_message "INFO" "Interactive mode"
echo "Let's perform a division. Enter positive numbers."
## Get user input safely
num1=$(get_number "Enter first number")
num2=$(get_number "Enter second number")
## Perform division
result=$(divide "$num1" "$num2")
exit_code=$?
if [ $exit_code -eq 0 ]; then
log_message "INFO" "$num1 / $num2 = $result"
else
log_message "ERROR" "Division failed with code $exit_code"
fi
log_message "INFO" "Calculator finished"
Guarda, hazlo ejecutable y ejecuta el script:
chmod +x error_handling.sh
./error_handling.sh
Verás una salida similar a la siguiente y se te pedirá que ingreses números:
[2023-11-04 13:45:23] [INFO] Starting division calculator
[2023-11-04 13:45:23] [INFO] 10 / 2 = 5.00
[2023-11-04 13:45:23] [INFO] Testing with invalid values
[2023-11-04 13:45:23] [ERROR] Numerator must be positive
[2023-11-04 13:45:23] [INFO] Exit code: 2
[2023-11-04 13:45:23] [ERROR] Division by zero is not allowed
[2023-11-04 13:45:23] [INFO] Exit code: 3
[2023-11-04 13:45:23] [ERROR] Numerator must be a number
[2023-11-04 13:45:23] [INFO] Exit code: 1
[2023-11-04 13:45:23] [INFO] Interactive mode
Let's perform a division. Enter positive numbers.
Enter first number:
Ingresa un número, por ejemplo 20
. Luego se te pedirá el segundo número:
Enter second number:
Ingresa otro número, por ejemplo 4
, y deberías ver:
[2023-11-04 13:45:30] [INFO] 20 / 4 = 5.00
[2023-11-04 13:45:30] [INFO] Calculator finished
Mejores prácticas para funciones en Bash
Basándonos en nuestros ejemplos, aquí están algunas mejores prácticas para trabajar con funciones en Bash:
- Agrega comentarios descriptivos - Documenta lo que hace cada función, sus parámetros y valores de retorno.
- Usa nombres de función significativos - Elige nombres que indiquen claramente el propósito de la función.
- Valida los parámetros de entrada - Verifica las entradas para prevenir errores.
- Usa variables locales - Evita colisiones de nombres de variables con la palabra clave
local
.
- Devuelve códigos de salida adecuados - Utiliza códigos de retorno convencionales (0 para éxito, distinto de cero para errores).
- Implementa un manejo adecuado de errores - Registra los errores y manéjalos con gracia.
- Mantén las funciones enfocadas - Cada función debe hacer una cosa bien.
- Usa la composición de funciones - Construye funcionalidad compleja combinando funciones más simples.
- Documenta los valores de retorno - Documenta claramente cómo se devuelven los valores (a través de echo, código de retorno, etc.).
- Prueba los casos extremos - Asegúrate de que las funciones manejen correctamente entradas inusuales.
Siguiendo estas prácticas, puedes crear funciones en Bash más confiables, mantenibles y reutilizables.
Creando una biblioteca de funciones
Para el último ejercicio, vamos a crear una biblioteca de funciones reutilizable:
touch math_functions.lib
Agrega el siguiente contenido:
#!/bin/bash
## math_functions.lib - A library of mathematical functions
## Add two numbers
add() {
echo $(($1 + $2))
}
## Subtract second number from first
subtract() {
echo $(($1 - $2))
}
## Multiply two numbers
multiply() {
echo $(($1 * $2))
}
## Divide first number by second (with decimal precision)
divide() {
if [ "$2" -eq 0 ]; then
return 1
fi
echo "scale=2; $1 / $2" | bc
return 0
}
## Calculate power: first number raised to second number
power() {
echo $(($1 ** $2))
}
## Check if a number is even
is_even() {
if (($1 % 2 == 0)); then
return 0
else
return 1
fi
}
## Check if a number is odd
is_odd() {
if is_even "$1"; then
return 1
else
return 0
fi
}
Ahora crea un script que use esta biblioteca:
touch use_library.sh
Agrega el siguiente contenido:
#!/bin/bash
## Source the math functions library
source math_functions.lib
## Display a header
echo "Math Functions Demo"
echo "------------------"
## Test the functions
echo "Addition: 5 + 3 = $(add 5 3)"
echo "Subtraction: 10 - 4 = $(subtract 10 4)"
echo "Multiplication: 6 * 7 = $(multiply 6 7)"
## Test division with error handling
div_result=$(divide 20 5)
if [ $? -eq 0 ]; then
echo "Division: 20 / 5 = $div_result"
else
echo "Division error: Cannot divide by zero"
fi
## Test division by zero
div_result=$(divide 20 0)
if [ $? -eq 0 ]; then
echo "Division: 20 / 0 = $div_result"
else
echo "Division error: Cannot divide by zero"
fi
echo "Power: 2 ^ 8 = $(power 2 8)"
## Test the even/odd functions
echo ""
echo "Number properties:"
for num in 1 2 3 4 5; do
echo -n "Number $num is "
if is_even $num; then
echo "even"
else
echo "odd"
fi
done
Guarda, hazlo ejecutable y ejecuta el script:
chmod +x use_library.sh
./use_library.sh
Deberías ver:
Math Functions Demo
------------------
Addition: 5 + 3 = 8
Subtraction: 10 - 4 = 6
Multiplication: 6 * 7 = 42
Division: 20 / 5 = 4.00
Division error: Cannot divide by zero
Power: 2 ^ 8 = 256
Number properties:
Number 1 is odd
Number 2 is even
Number 3 is odd
Number 4 is even
Number 5 is odd
Este enfoque de biblioteca demuestra cómo se pueden crear colecciones de funciones reutilizables que se pueden importar en múltiples scripts, promoviendo la reutilización de código y la mantenibilidad.