11  Control de Flujo

Este módulo cubre las estructuras de decisión y bucles para automatizar tareas bioinformáticas complejas.

11.1 Estructuras de Decisión

11.1.1 if...then...elif...else

Validación robusta de archivos de entrada:

#!/bin/bash
ref="genome.fa"
reads="sample_R1.fastq"

# Validación completa de archivos
if [[ ! -f "$ref" ]]; then
    echo "ERROR: Referencia $ref no encontrada" >&2
    exit 1
elif [[ ! -s "$ref" ]]; then
    echo "ERROR: Referencia $ref está vacía" >&2
    exit 1
elif [[ ! -r "$ref" ]]; then
    echo "ERROR: Sin permisos de lectura en $ref" >&2
    exit 1
else
    echo "✓ Usando referencia válida: $ref"
fi

# Verificar calidad de reads
if [[ -f "$reads" ]]; then
    read_count=$(wc -l < "$reads")
    if (( read_count < 1000 )); then
        echo "⚠️  Pocas lecturas detectadas: $((read_count/4)) reads"
    else
        echo "✓ Archivo de reads válido: $((read_count/4)) reads"
    fi
fi

11.1.2 Operadores de Comparación

# Comparaciones numéricas
coverage=25
if (( coverage >= 30 )); then
    echo "Cobertura alta: ${coverage}x"
elif (( coverage >= 10 )); then
    echo "Cobertura media: ${coverage}x"
else
    echo "Cobertura baja: ${coverage}x - considerar re-secuenciación"
fi

# Comparaciones de strings
formato="fastq"
if [[ "$formato" == "fastq" || "$formato" == "fq" ]]; then
    echo "Formato de secuenciación detectado"
fi

11.2 Estructura case

Procesamiento por tipo de archivo biológico:

#!/bin/bash
procesar_archivo() {
    local archivo="$1"
    local extension="${archivo##*.}"
    
    case "$extension" in
        fasta|fa|fas)
            echo "🧬 Procesando secuencia FASTA: $archivo"
            # Contar secuencias
            grep -c "^>" "$archivo" && echo "secuencias encontradas"
            ;;
        fastq|fq)
            echo "📊 Procesando reads FASTQ: $archivo"
            # QC rápido
            total_reads=$(($(wc -l < "$archivo") / 4))
            echo "Total de reads: $total_reads"
            ;;
        bam)
            echo "🗂️  Procesando alineamiento BAM: $archivo"
            samtools flagstat "$archivo" 2>/dev/null || echo "Archivo BAM corrupto"
            ;;
        vcf|vcf.gz)
            echo "🔬 Procesando variantes VCF: $archivo"
            if [[ "$archivo" == *.gz ]]; then
                zcat "$archivo" | grep -v "^#" | wc -l
            else
                grep -v "^#" "$archivo" | wc -l
            fi
            echo "variantes encontradas"
            ;;
        bed)
            echo "📍 Procesando regiones BED: $archivo"
            wc -l < "$archivo" && echo "regiones definidas"
            ;;
        *)
            echo "⚠️  Tipo de archivo desconocido: $archivo"
            echo "Extensiones soportadas: fasta, fastq, bam, vcf, bed"
            return 1
            ;;
    esac
}

# Uso
for archivo in data/*; do
    [[ -f "$archivo" ]] && procesar_archivo "$archivo"
done

11.3 Bucles

11.3.1 Bucle for - Procesamiento por lotes

#!/bin/bash
# Pipeline de QC para múltiples muestras
samples=(SRR001 SRR002 SRR003 SRR004)
reference="hg38.fa"

echo "🚀 Iniciando pipeline de QC y alineamiento"

for sample in "${samples[@]}"; do
    echo "
=== Procesando muestra: $sample ==="
    
    # 1. Control de calidad
    if [[ -f "raw_data/${sample}_R1.fastq.gz" ]]; then
        echo "📊 Ejecutando FastQC..."
        fastqc "raw_data/${sample}_R1.fastq.gz" "raw_data/${sample}_R2.fastq.gz" \
               -o "qc_reports/" --quiet
    fi
    
    # 2. Alineamiento
    echo "🎯 Alineando contra referencia..."
    bwa mem -t 4 "$reference" \
        "raw_data/${sample}_R1.fastq.gz" \
        "raw_data/${sample}_R2.fastq.gz" | \
    samtools sort -@ 4 -o "alignments/${sample}.sorted.bam"
    
    # 3. Indexado
    echo "📇 Indexando BAM..."
    samtools index "alignments/${sample}.sorted.bam"
    
    echo "✅ $sample completado"
done

echo "🎉 Pipeline finalizado para ${#samples[@]} muestras"

11.3.2 Bucle while - Lectura de archivos

#!/bin/bash
# Procesar lista de muestras desde archivo
sample_list="samples.txt"

echo "📋 Procesando muestras desde: $sample_list"

while IFS=$'\t' read -r sample_id condition replicate; do
    # Saltar líneas de comentario
    [[ "$sample_id" =~ ^#.*$ ]] && continue
    
    echo "Procesando: $sample_id (Condición: $condition, Rep: $replicate)"
    
    # Crear directorio específico
    mkdir -p "results/${condition}/${sample_id}"
    
    # Simular análisis
    echo "Análisis de expresión diferencial para $sample_id" > \
         "results/${condition}/${sample_id}/analysis.log"
         
done < "$sample_list"

# Ejemplo de samples.txt:
# sample_id    condition    replicate
# CTRL_01      control      1
# CTRL_02      control      2
# TREAT_01     treatment    1
# TREAT_02     treatment    2

11.3.3 Bucle until - Monitoreo de procesos

#!/bin/bash
# Monitorear finalización de trabajo en cluster
job_name="blast_search"

echo "⏳ Esperando finalización de trabajo: $job_name"

until ! pgrep -f "$job_name" > /dev/null; do
    # Mostrar progreso cada 30 segundos
    echo "$(date '+%H:%M:%S') - $job_name aún ejecutándose..."
    
    # Verificar uso de memoria
    memory_usage=$(ps -eo pid,comm,pmem | grep "$job_name" | awk '{sum+=$3} END {print sum}')
    if [[ -n "$memory_usage" ]] && (( $(echo "$memory_usage > 80" | bc -l) )); then
        echo "⚠️  Alto uso de memoria: ${memory_usage}%"
    fi
    
    sleep 30
done

echo "✅ Trabajo $job_name finalizado en $(date)"

11.4 Control de Bucles

11.4.1 break y continue

#!/bin/bash
# Procesamiento con manejo de errores
fastq_files=(*.fastq)

for file in "${fastq_files[@]}"; do
    echo "Verificando: $file"
    
    # Saltar archivos vacíos
    if [[ ! -s "$file" ]]; then
        echo "⚠️  Archivo vacío, saltando: $file"
        continue
    fi
    
    # Verificar formato FASTQ básico
    if ! head -n 4 "$file" | grep -q "^@"; then
        echo "❌ Formato FASTQ inválido: $file"
        continue
    fi
    
    # Verificar si ya fue procesado
    if [[ -f "${file%.fastq}.processed" ]]; then
        echo "✅ Ya procesado: $file"
        continue
    fi
    
    # Simular error crítico
    if [[ "$file" == *"corrupted"* ]]; then
        echo "💥 Error crítico en $file - deteniendo pipeline"
        break
    fi
    
    # Procesar archivo
    echo "🔄 Procesando: $file"
    # Aquí iría el procesamiento real
    touch "${file%.fastq}.processed"
done

11.5 Ejemplo Bioinformático Integrado

Pipeline completo de análisis de variantes:

#!/bin/bash
set -euo pipefail

# Configuración
reference="data/reference/hg38.fa"
samples_dir="data/samples"
results_dir="results"
min_coverage=10

# Crear estructura de directorios
mkdir -p "$results_dir"/{qc,alignments,variants,reports}

echo "🧬 Pipeline de Análisis de Variantes Iniciado"
echo "=================================="

# Validar archivos de entrada
if [[ ! -f "$reference" ]]; then
    echo "❌ Archivo de referencia no encontrado: $reference"
    exit 1
fi

# Procesar cada muestra
for sample_dir in "$samples_dir"/*/; do
    sample_name=$(basename "$sample_dir")
    
    echo "
📊 Procesando muestra: $sample_name"
    
    # Buscar archivos FASTQ
    r1_file=$(find "$sample_dir" -name "*R1*.fastq*" | head -1)
    r2_file=$(find "$sample_dir" -name "*R2*.fastq*" | head -1)
    
    if [[ -z "$r1_file" || -z "$r2_file" ]]; then
        echo "⚠️  Archivos FASTQ incompletos para $sample_name, saltando..."
        continue
    fi
    
    # QC y alineamiento
    case "${r1_file##*.}" in
        gz)
            echo "🗜️  Archivos comprimidos detectados"
            reads_count=$(($(zcat "$r1_file" | wc -l) / 4))
            ;;
        fastq)
            reads_count=$(($(wc -l < "$r1_file") / 4))
            ;;
        *)
            echo "❌ Formato no soportado: $r1_file"
            continue
            ;;
    esac
    
    echo "📈 Reads encontrados: $reads_count"
    
    # Solo procesar si hay suficientes reads
    if (( reads_count < 10000 )); then
        echo "⚠️  Muy pocos reads ($reads_count), saltando $sample_name"
        continue
    fi
    
    # Alineamiento y llamada de variantes (simulado)
    bam_file="$results_dir/alignments/${sample_name}.bam"
    vcf_file="$results_dir/variants/${sample_name}.vcf"
    
    echo "🎯 Generando alineamiento..."
    echo "Simulated BAM for $sample_name" > "$bam_file"
    
    echo "🔬 Llamando variantes..."
    echo "Simulated VCF for $sample_name" > "$vcf_file"
    
    echo "✅ $sample_name procesado exitosamente"
done

echo "
🎉 Pipeline completado. Resultados en: $results_dir"

11.6 Buenas Prácticas

11.6.1 Validación y Manejo de Errores

#!/bin/bash
# Función para validar herramientas
check_dependencies() {
    local tools=("samtools" "bwa" "fastqc")
    local missing=()
    
    for tool in "${tools[@]}"; do
        if ! command -v "$tool" &> /dev/null; then
            missing+=("$tool")
        fi
    done
    
    if (( ${#missing[@]} > 0 )); then
        echo "❌ Herramientas faltantes: ${missing[*]}"
        echo "Instala con: conda install ${missing[*]}"
        exit 1
    fi
    
    echo "✅ Todas las dependencias disponibles"
}

# Función para progreso visual
show_progress() {
    local current=$1
    local total=$2
    local percent=$((current * 100 / total))
    local bar_length=20
    local filled=$((percent * bar_length / 100))
    
    printf "\rProgreso: ["
    printf "%*s" $filled | tr ' ' '█'
    printf "%*s" $((bar_length - filled)) | tr ' ' '░'
    printf "] %d%% (%d/%d)" $percent $current $total
}

# Uso en bucle
samples=(sample1 sample2 sample3 sample4 sample5)
total=${#samples[@]}

for i in "${!samples[@]}"; do
    show_progress $((i + 1)) $total
    # Simular procesamiento
    sleep 1
done
echo -e "\n✅ Procesamiento completo"

11.7 Resumen de Comandos

Estructura Sintaxis Uso Principal
if if [[ condition ]]; then ... fi Decisiones condicionales
case case $var in pattern) ... ;; esac Múltiples opciones
for for item in list; do ... done Iteración sobre elementos
while while condition; do ... done Bucle con condición previa
until until condition; do ... done Bucle hasta condición
break break Salir de bucle
continue continue Siguiente iteración

Operadores útiles: - (( )) para comparaciones numéricas - [[ ]] para comparaciones de strings y archivos - && y || para lógica booleana