Control de Flujo en Bucles
En ocasiones, necesitamos más control sobre cómo se ejecutan nuestros bucles. Bash proporciona dos comandos fundamentales para este propósito: break y continue. Estas herramientas nos permiten modificar el comportamiento normal de los bucles para crear lógica más sofisticada y eficiente.
Comandos de Control de Bucles
BREAK
Función: Termina completamente el bucle actual y continúa con el código después del bucle.
Uso: Cuando se cumple una condición que hace innecesario continuar con las iteraciones.
CONTINUE
Función: Salta el resto de la iteración actual y pasa directamente a la siguiente.
Uso: Cuando quieres omitir ciertos elementos pero continuar con el bucle.
Flujo de Control Visual
Sale del bucle
Siguiente iteración
El Comando BREAK
El comando break termina inmediatamente el bucle más interno y continúa la ejecución con la primera línea después del bucle.
Sintaxis y Uso Básico
for variable in lista
do
# código antes de la condición
if [ condición ]
then
break # Sale completamente del bucle
fi
# código que se ejecuta si no se hace break
done
# El programa continúa aquí después del break
Ejemplos Prácticos
#!/bin/bash
# Buscar un archivo específico y parar cuando se encuentre
archivo_buscado="config.txt"
encontrado=false
echo "Buscando $archivo_buscado en directorios..."
for directorio in /etc /home /var /tmp
do
echo "Buscando en: $directorio"
if [ -f "$directorio/$archivo_buscado" ]; then
echo "¡Archivo encontrado en $directorio/$archivo_buscado!"
encontrado=true
break # Ya lo encontramos, no necesitamos seguir buscando
fi
echo "No encontrado en $directorio"
done
if [ "$encontrado" = true ]; then
echo "Búsqueda completada exitosamente"
else
echo "Archivo no encontrado en ningún directorio"
fi
$ ./buscar_archivo.sh
Buscando config.txt en directorios...
Buscando en: /etc
No encontrado en /etc
Buscando en: /home
¡Archivo encontrado en /home/config.txt!
Búsqueda completada exitosamente
#!/bin/bash
# Pedir entrada válida al usuario
while true
do
echo -n "Ingresa un número entre 1 y 10 (o 'q' para salir): "
read entrada
# Verificar si el usuario quiere salir
if [ "$entrada" = "q" ] || [ "$entrada" = "Q" ]; then
echo "¡Hasta luego!"
break
fi
# Verificar si es un número válido
if [[ "$entrada" =~ ^[0-9]+$ ]] && [ "$entrada" -ge 1 ] && [ "$entrada" -le 10 ]; then
echo "¡Excelente! Elegiste el número $entrada"
break
else
echo "Error: Debes ingresar un número entre 1 y 10"
fi
done
echo "Programa terminado"
Consejo Pro
Usa break para evitar procesamiento innecesario. Es especialmente útil en búsquedas donde una vez que encuentras lo que buscas, no necesitas continuar.
El Comando CONTINUE
El comando continue omite el resto de la iteración actual y pasa inmediatamente a la siguiente iteración del bucle.
Sintaxis y Uso Básico
for variable in lista
do
if [ condición_de_omisión ]
then
continue # Salta a la siguiente iteración
fi
# código que se ejecuta solo si no se hace continue
done
Ejemplos Prácticos
#!/bin/bash
# Procesar solo archivos .txt que no estén vacíos
echo "Procesando archivos de texto..."
for archivo in *.txt
do
# Omitir si el archivo no existe (caso de no hay archivos .txt)
if [ ! -f "$archivo" ]; then
continue
fi
# Omitir archivos vacíos
if [ ! -s "$archivo" ]; then
echo "Omitiendo $archivo (archivo vacío)"
continue
fi
# Omitir archivos de respaldo (que terminen en .bak.txt)
if [[ "$archivo" == *.bak.txt ]]; then
echo "Omitiendo $archivo (archivo de respaldo)"
continue
fi
# Procesar archivo válido
lineas=$(wc -l < "$archivo")
palabras=$(wc -w < "$archivo")
echo "Procesando $archivo: $lineas líneas, $palabras palabras"
done
echo "Procesamiento completado"
$ ./procesar_textos.sh
Procesando archivos de texto...
Omitiendo temporal.txt (archivo vacío)
Omitiendo backup.bak.txt (archivo de respaldo)
Procesando documento.txt: 25 líneas, 180 palabras
Procesando notas.txt: 15 líneas, 95 palabras
Procesamiento completado
#!/bin/bash
# Procesar solo números pares del 1 al 20
echo "Procesando números pares del 1 al 20:"
for numero in {1..20}
do
# Omitir números impares
if [ $((numero % 2)) -ne 0 ]; then
continue
fi
# Procesar solo números pares
cuadrado=$((numero * numero))
echo "El cuadrado de $numero es $cuadrado"
done
Ventaja del continue
El comando continue te permite filtrar elementos sin necesidad de anidar múltiples estructuras if, manteniendo tu código más limpio y legible.
Break y Continue en Bucles Anidados
Cuando trabajas con bucles anidados, break y continue afectan solo al bucle más interno por defecto. Sin embargo, puedes especificar cuántos niveles quieres afectar.
Sintaxis con Niveles
# break n - Sale de n niveles de bucle
# continue n - Continúa n niveles de bucle hacia arriba
for i in {1..3}
do
for j in {1..3}
do
if [ condición ]; then
break 2 # Sale de ambos bucles (2 niveles)
fi
if [ otra_condición ]; then
continue 2 # Continúa con la siguiente iteración del bucle externo
fi
done
done
#!/bin/bash
# Buscar un número específico en una matriz
numero_buscado=7
encontrado=false
echo "Buscando el número $numero_buscado en matriz 3x3:"
for fila in 1 2 3
do
for columna in 1 2 3
do
# Simular valores de matriz
valor=$(((fila - 1) * 3 + columna))
echo -n "Revisando posición [$fila,$columna] = $valor ... "
if [ $valor -eq $numero_buscado ]; then
echo "¡ENCONTRADO!"
encontrado=true
break 2 # Sale de ambos bucles
else
echo "no es"
fi
done
done
if [ "$encontrado" = true ]; then
echo "Búsqueda completada: número encontrado"
else
echo "Búsqueda completada: número no encontrado"
fi
$ ./buscar_matriz.sh
Buscando el número 7 en matriz 3x3:
Revisando posición [1,1] = 1 ... no es
Revisando posición [1,2] = 2 ... no es
Revisando posición [1,3] = 3 ... no es
Revisando posición [2,1] = 4 ... no es
Revisando posición [2,2] = 5 ... no es
Revisando posición [2,3] = 6 ... no es
Revisando posición [3,1] = 7 ... ¡ENCONTRADO!
Búsqueda completada: número encontrado
Casos de Uso Avanzados
Validación de Datos
#!/bin/bash
# Validar lista de emails
emails=("[email protected]" "invalid-email" "[email protected]" "" "[email protected]")
echo "Validando emails:"
emails_validos=0
for email in "${emails[@]}"
do
# Omitir emails vacíos
if [ -z "$email" ]; then
echo "Omitiendo email vacío"
continue
fi
# Validación básica (contiene @ y .)
if [[ "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
echo "✓ Email válido: $email"
((emails_validos++))
else
echo "✗ Email inválido: $email"
continue
fi
done
echo "Total de emails válidos: $emails_validos"
Descarga con Reintentos
#!/bin/bash
# Descargar archivos con reintentos
urls=("http://example.com/file1.txt" "http://example.com/file2.txt")
for url in "${urls[@]}"
do
archivo=$(basename "$url")
echo "Descargando: $archivo"
# Intentar descargar hasta 3 veces
for intento in {1..3}
do
if curl -f -o "$archivo" "$url"; then
echo "✓ Descarga exitosa en intento $intento"
break # Éxito, pasar al siguiente archivo
else
echo "✗ Intento $intento fallido"
if [ $intento -eq 3 ]; then
echo "Error: No se pudo descargar $archivo"
else
echo "Reintentando en 2 segundos..."
sleep 2
fi
fi
done
done
Errores Comunes y Mejores Prácticas
Errores Comunes
- Break fuera de bucle: Usar break fuera de un bucle causa error
- Nivel incorrecto: break 5 cuando solo hay 2 niveles
- Lógica confusa: Usar continue cuando se necesita break
- Bucles infinitos: Continue que impide que cambie la condición
Mejores Prácticas
- Usa nombres descriptivos: Variables que indican el estado
- Comenta la lógica: Explica por qué usas break/continue
- Mantén simplicidad: Evita demasiados niveles anidados
- Valida condiciones: Asegúrate de que las condiciones pueden cambiar
#!/bin/bash
# Ejemplo con buenas prácticas
archivos_procesados=0
archivos_omitidos=0
for archivo in *.log
do
# Verificar que el archivo existe (globbing puede no encontrar archivos)
if [ ! -f "$archivo" ]; then
echo "No hay archivos .log para procesar"
break
fi
# Omitir archivos de sistema (empiezan con punto)
if [[ "$archivo" == .* ]]; then
echo "Omitiendo archivo de sistema: $archivo"
((archivos_omitidos++))
continue
fi
# Omitir archivos muy grandes (>100MB)
if [ $(stat -f%z "$archivo" 2>/dev/null || stat -c%s "$archivo" 2>/dev/null) -gt 104857600 ]; then
echo "Omitiendo archivo muy grande: $archivo"
((archivos_omitidos++))
continue
fi
# Procesar archivo válido
echo "Procesando: $archivo"
# Aquí iría la lógica de procesamiento
((archivos_procesados++))
done
echo "Resumen:"
echo "- Archivos procesados: $archivos_procesados"
echo "- Archivos omitidos: $archivos_omitidos"
Ejercicios Prácticos
Ejercicio 1: Calculadora de Promedios
Crea un script que calcule promedios omitiendo valores inválidos:
#!/bin/bash
# Calcular promedio de números válidos
numeros=("10" "abc" "20" "" "30" "-5" "40")
suma=0
contador=0
echo "Calculando promedio de números válidos:"
for numero in "${numeros[@]}"
do
# Omitir strings vacíos
if [ -z "$numero" ]; then
echo "Omitiendo valor vacío"
continue
fi
# Verificar si es un número
if ! [[ "$numero" =~ ^-?[0-9]+$ ]]; then
echo "Omitiendo valor no numérico: $numero"
continue
fi
# Omitir números negativos (según requisitos del ejemplo)
if [ "$numero" -lt 0 ]; then
echo "Omitiendo número negativo: $numero"
continue
fi
# Procesar número válido
echo "Usando número: $numero"
suma=$((suma + numero))
((contador++))
done
if [ $contador -gt 0 ]; then
promedio=$((suma / contador))
echo "Promedio de $contador números: $promedio"
else
echo "No se encontraron números válidos"
fi
Ejercicio 2: Generador de Contraseñas
Crea un generador que produzca contraseñas hasta encontrar una que cumpla criterios específicos:
#!/bin/bash
# Generar contraseña que cumpla criterios específicos
intentos=0
max_intentos=100
echo "Generando contraseña segura..."
while [ $intentos -lt $max_intentos ]
do
((intentos++))
# Generar contraseña aleatoria de 12 caracteres
password=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 12)
echo "Intento $intentos: $password"
# Verificar longitud mínima
if [ ${#password} -lt 8 ]; then
echo " ✗ Muy corta"
continue
fi
# Verificar que tenga al menos una mayúscula
if ! [[ "$password" =~ [A-Z] ]]; then
echo " ✗ Sin mayúsculas"
continue
fi
# Verificar que tenga al menos una minúscula
if ! [[ "$password" =~ [a-z] ]]; then
echo " ✗ Sin minúsculas"
continue
fi
# Verificar que tenga al menos un número
if ! [[ "$password" =~ [0-9] ]]; then
echo " ✗ Sin números"
continue
fi
# Si llegamos aquí, la contraseña cumple todos los criterios
echo " ✓ ¡Contraseña válida generada en $intentos intentos!"
echo " Contraseña final: $password"
break
done
if [ $intentos -eq $max_intentos ]; then
echo "No se pudo generar una contraseña válida en $max_intentos intentos"
exit 1
fi