Redirecciones y Pipes

Domina el flujo de datos entre comandos y archivos en Bash

Módulo 2 ⏱️ 30-35 min 🔄 I/O ⚡ Pipes 📚 Principiante

Introducción a las Redirecciones

En Bash, las redirecciones y pipes son mecanismos fundamentales para manipular el flujo de datos. Permiten dirigir la salida de un comando hacia un archivo, tomar entrada desde un archivo, o conectar múltiples comandos para crear flujos de procesamiento complejos.

Todo proceso en Unix/Linux tiene tres flujos de datos estándar:

  • stdin (0): Entrada estándar - donde el proceso lee datos
  • stdout (1): Salida estándar - donde el proceso escribe resultados normales
  • stderr (2): Error estándar - donde el proceso escribe mensajes de error
Flujos de Datos Estándar
stdin (0)
PROCESO
stdout (1)
stderr (2)

Redirecciones Básicas

Operadores de Redirección

Operador Significado Ejemplo Descripción
> Redirigir salida ls > archivo.txt Guarda la salida en archivo (sobrescribe)
>> Agregar salida echo "texto" >> archivo.txt Agrega la salida al final del archivo
< Redirigir entrada sort < archivo.txt Toma entrada desde archivo
2> Redirigir errores comando 2> errores.txt Guarda errores en archivo
2>> Agregar errores comando 2>> errores.txt Agrega errores al archivo
&> Redirigir todo comando &> todo.txt Redirige stdout y stderr
# Ejemplos básicos de redirección
$ echo "¡Hola mundo!" > saludo.txt
$ cat saludo.txt
¡Hola mundo!

# Agregar contenido sin sobrescribir
$ echo "Segunda línea" >> saludo.txt
$ cat saludo.txt
¡Hola mundo!
Segunda línea

# Redirigir lista de archivos a archivo
$ ls -la > lista_archivos.txt
$ head -5 lista_archivos.txt
total 24
drwxr-xr-x 2 user user 4096 oct 15 10:30 .
drwxr-xr-x 5 user user 4096 oct 15 10:25 ..
-rw-r--r-- 1 user user   30 oct 15 10:31 lista_archivos.txt
-rw-r--r-- 1 user user   27 oct 15 10:30 saludo.txt

Manejo de Errores

Una de las características más poderosas de las redirecciones es la capacidad de manejar errores de forma independiente de la salida normal:

# Intentar listar un directorio que no existe
$ ls directorio_inexistente
ls: cannot access 'directorio_inexistente': No such file or directory

# Redirigir solo los errores
$ ls directorio_inexistente 2> errores.txt
$ cat errores.txt
ls: cannot access 'directorio_inexistente': No such file or directory

# Redirigir salida normal y errores a archivos diferentes
$ ls /home /directorio_inexistente > salida.txt 2> errores.txt
$ cat salida.txt
/home:
usuario1
usuario2
$ cat errores.txt
ls: cannot access '/directorio_inexistente': No such file or directory

# Redirigir tanto salida como errores al mismo archivo
$ ls /home /directorio_inexistente > todo.txt 2>&1
$ cat todo.txt
ls: cannot access '/directorio_inexistente': No such file or directory
/home:
usuario1
usuario2
Importante

El operador 2>&1 redirige stderr (2) hacia donde ya va stdout (1). El orden importa: > archivo.txt 2>&1 funciona, pero 2>&1 > archivo.txt no hará lo que esperas.

Pipes: Conectando Comandos

Los pipes (tubería, simbolizada por |) permiten conectar la salida de un comando con la entrada de otro, creando cadenas de procesamiento eficientes.

Funcionamiento de un Pipe
comando1
|
comando2
|
comando3

La salida de comando1 se convierte en la entrada de comando2, y así sucesivamente

# Ejemplos básicos de pipes
$ ls -la | grep ".txt"
-rw-r--r-- 1 user user   30 oct 15 10:31 lista_archivos.txt
-rw-r--r-- 1 user user   27 oct 15 10:30 saludo.txt

# Contar líneas de archivos de texto
$ ls -la | grep ".txt" | wc -l
2

# Ver procesos y filtrar por usuario
$ ps aux | grep $USER | head -5
user     1234  0.0  0.1  12345  6789 ?    S    10:30   0:00 bash
user     1235  0.0  0.2  23456  7890 ?    S    10:30   0:01 firefox
user     1236  0.1  0.3  34567  8901 ?    R    10:31   0:02 chrome

# Cadena más compleja: buscar archivos grandes
$ find /home/$USER -type f | xargs ls -la | sort -k5 -nr | head -10

Pipes Comunes y Útiles

Combinaciones Frecuentes

Pipes más utilizados en administración Copiar
# 1. Análisis de logs
tail -f /var/log/syslog | grep "error"

# 2. Procesos consumiendo más memoria
ps aux | sort -k4 -nr | head -10

# 3. Encontrar archivos duplicados por tamaño
find . -type f | xargs ls -la | sort -k5 -n | uniq -D -f4

# 4. Contar tipos de archivos
find . -type f | grep -E '\.[^.]+$' | sed 's/.*\.//' | sort | uniq -c | sort -nr

# 5. Monitoreo de espacio en disco
df -h | grep -v tmpfs | sort -k5 -nr

# 6. Top 10 de comandos más usados
history | awk '{print $2}' | sort | uniq -c | sort -nr | head -10

# 7. Buscar texto en múltiples archivos
grep -r "función" . | cut -d: -f1 | sort | uniq

# 8. Análisis de conexiones de red
netstat -an | grep ESTABLISHED | wc -l
Tip Profesional

Los pipes son una de las características más poderosas de Unix/Linux. Dominar combinaciones como grep, sort, uniq, cut, y awk te convertirá en un usuario mucho más eficiente.

Redirecciones Avanzadas

Here Documents y Here Strings

Bash ofrece métodos avanzados para proporcionar entrada a comandos usando here documents (<<) y here strings (<<<):

# Here Document - entrada multilínea
$ cat << EOF > mi_archivo.txt
Esta es la línea 1
Esta es la línea 2
Línea con variable: $USER
EOF

$ cat mi_archivo.txt
Esta es la línea 1
Esta es la línea 2
Línea con variable: usuario

# Here Document sin expansión de variables (EOF entre comillas)
$ cat << 'EOF' > literal.txt
Esta línea contiene $USER literal
No se expande la variable
EOF

# Here String - entrada de una línea
$ grep "bash" <<< "Mi shell favorito es bash"
Mi shell favorito es bash

# Usar here string con variables
$ nombre="Juan"
$ grep "Juan" <<< "El usuario es $nombre"
El usuario es Juan

Redirecciones con Descriptores de Archivo

# Abrir archivo para lectura (descriptor 3)
$ exec 3< mi_archivo.txt
$ read -u 3 linea
$ echo $linea
Esta es la línea 1

# Abrir archivo para escritura (descriptor 4)
$ exec 4> salida_personalizada.txt
$ echo "Escribiendo en descriptor 4" >&4
$ exec 4>&-  # Cerrar el descriptor

# Intercambiar stdout y stderr
$ comando 3>&1 1>&2 2>&3 3>&-
Descriptores de Archivo

Los descriptores de archivo son números que el sistema usa para referenciar archivos abiertos. 0, 1, y 2 están reservados para stdin, stdout, y stderr respectivamente. Puedes usar descriptores del 3 al 9 para tus propios propósitos.

Trucos y Técnicas Avanzadas

Tee: Duplicar el Flujo

El comando tee permite ver la salida en pantalla mientras también la guardas en un archivo:

# Ver y guardar al mismo tiempo
$ ls -la | tee lista.txt
total 16
drwxr-xr-x 2 user user 4096 oct 15 11:00 .
drwxr-xr-x 5 user user 4096 oct 15 10:25 ..
-rw-r--r-- 1 user user    0 oct 15 11:00 lista.txt
-rw-r--r-- 1 user user   27 oct 15 10:30 saludo.txt

# Agregar con tee (opción -a)
$ date | tee -a lista.txt
vie oct 15 11:00:00 UTC 2021

# Dividir salida hacia múltiples archivos
$ echo "Información importante" | tee archivo1.txt archivo2.txt archivo3.txt

Process Substitution

La sustitución de procesos <(...) y >(...) permite usar la salida de un comando como si fuera un archivo:

# Comparar salidas de dos comandos
$ diff <(ls /bin) <(ls /usr/bin) | head -5
1,3c1,3
< bash
< cat
< chgrp
---
> X11
> [
> aa-enabled

# Usar salida de comando como archivo de entrada
$ sort <(echo -e "c\na\nb")
a
b
c

# Process substitution para salida
$ echo "contenido" > >(cat > nuevo_archivo.txt)
$ cat nuevo_archivo.txt
contenido
Nota sobre Portabilidad

Process substitution es una característica específica de Bash y no está disponible en todos los shells. Si necesitas compatibilidad con sh, usa archivos temporales en su lugar.

Casos Prácticos y Ejemplos Reales

Scripts de administración del sistema Copiar
#!/bin/bash
# Script de respaldo con logs detallados

BACKUP_DIR="/backup"
LOG_FILE="/var/log/backup.log"
ERROR_FILE="/var/log/backup_errors.log"

# Función que hace respaldo y registra todo
hacer_respaldo() {
    echo "=== Iniciando respaldo $(date) ===" | tee -a "$LOG_FILE"
    
    # Respaldo con logs de progreso y errores separados
    tar -czf "$BACKUP_DIR/backup_$(date +%Y%m%d).tar.gz" /home 2> >(tee -a "$ERROR_FILE") | tee -a "$LOG_FILE"
    
    # Verificar si hubo errores
    if [ -s "$ERROR_FILE" ]; then
        echo "¡ADVERTENCIA: Se encontraron errores!" | tee -a "$LOG_FILE"
        echo "Revisa $ERROR_FILE para más detalles" | tee -a "$LOG_FILE"
    fi
    
    echo "=== Respaldo completado $(date) ===" | tee -a "$LOG_FILE"
}

# Análisis de logs del sistema
analizar_logs() {
    echo "Análisis de logs del sistema:" > informe.txt
    echo "=============================" >> informe.txt
    
    # Errores más frecuentes
    echo -e "\nErrores más frecuentes:" >> informe.txt
    grep -i error /var/log/syslog | cut -d' ' -f6- | sort | uniq -c | sort -nr | head -10 >> informe.txt
    
    # Actividad por hora
    echo -e "\nActividad por hora:" >> informe.txt
    grep "$(date '+%b %d')" /var/log/syslog | cut -d' ' -f3 | cut -d':' -f1 | sort | uniq -c >> informe.txt
    
    echo "Informe generado en informe.txt"
}
Monitoreo y análisis de rendimiento Copiar
#!/bin/bash
# Script de monitoreo del sistema

# Función que genera reporte del sistema
generar_reporte() {
    {
        echo "REPORTE DEL SISTEMA - $(date)"
        echo "================================="
        echo
        
        echo "MEMORIA:"
        free -h | grep -E "(Mem|Swap)"
        echo
        
        echo "DISCO:"
        df -h | grep -v tmpfs | sort -k5 -nr
        echo
        
        echo "TOP PROCESOS POR CPU:"
        ps aux --sort=-%cpu | head -6
        echo
        
        echo "TOP PROCESOS POR MEMORIA:"
        ps aux --sort=-%mem | head -6
        echo
        
        echo "CONEXIONES DE RED:"
        netstat -an | grep ESTABLISHED | wc -l | xargs echo "Conexiones activas:"
        
    } | tee "reporte_sistema_$(date +%Y%m%d_%H%M).txt"
}

# Monitoreo continuo con alertas
monitoreo_continuo() {
    while true; do
        # Verificar uso de CPU
        cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
        
        # Si CPU > 80%, registrar procesos
        if (( $(echo "$cpu_usage > 80" | bc -l) )); then
            {
                echo "ALERTA: CPU alto $(date)"
                ps aux --sort=-%cpu | head -10
                echo "========================"
            } >> alertas_sistema.log
        fi
        
        sleep 60
    done &
}

Ejercicios Prácticos

Ejercicio 1: Análisis de Archivos

Crea un script que analice los archivos en tu directorio home:

Tareas a realizar Copiar
# 1. Crear lista de todos los archivos con tamaños
find $HOME -type f -exec ls -la {} \; > inventario_archivos.txt

# 2. Encontrar los 10 archivos más grandes
find $HOME -type f -exec ls -la {} \; | sort -k5 -nr | head -10 > archivos_grandes.txt

# 3. Contar archivos por extensión
find $HOME -type f | grep -E '\.[^.]+$' | sed 's/.*\.//' | sort | uniq -c | sort -nr > extensiones.txt

# 4. Buscar archivos duplicados por tamaño
find $HOME -type f -exec ls -la {} \; | awk '{print $5, $NF}' | sort | uniq -d -f1 > posibles_duplicados.txt

# 5. Generar reporte combinado
{
    echo "REPORTE DE ANÁLISIS DE ARCHIVOS"
    echo "==============================="
    echo
    echo "ARCHIVOS MÁS GRANDES:"
    head -5 archivos_grandes.txt
    echo
    echo "EXTENSIONES MÁS COMUNES:"
    head -10 extensiones.txt
    echo
    echo "POSIBLES DUPLICADOS:"
    wc -l posibles_duplicados.txt
} | tee reporte_archivos.txt
Ejercicio 2: Log de Sistema

Analiza los logs del sistema y crea un dashboard en texto:

Análisis de logs Copiar
# 1. Análisis básico de syslog (donde esté disponible)
sudo tail -1000 /var/log/syslog 2>/dev/null | grep -i error | head -10 > errores_recientes.txt

# 2. Si no hay syslog, usar dmesg
dmesg | tail -100 | grep -i error > errores_kernel.txt 2>/dev/null || echo "Sin acceso a dmesg" > errores_kernel.txt

# 3. Análisis de conexiones
{
    echo "CONEXIONES DE RED ACTUALES:"
    netstat -an 2>/dev/null | grep ESTABLISHED | wc -l | xargs echo "Total establecidas:"
    echo
    echo "PUERTOS EN ESCUCHA:"
    netstat -tln 2>/dev/null | grep LISTEN | head -10
} > conexiones.txt 2>/dev/null || echo "netstat no disponible" > conexiones.txt

# 4. Dashboard final
{
    echo "DASHBOARD DEL SISTEMA - $(date)"
    echo "================================"
    echo
    echo "ERRORES RECIENTES:"
    cat errores_recientes.txt errores_kernel.txt 2>/dev/null | head -5
    echo
    echo "ESTADO DE RED:"
    cat conexiones.txt
    echo
    echo "USO DE RECURSOS:"
    free -h 2>/dev/null | head -2 || echo "free no disponible"
    df -h . | tail -1
} | tee dashboard_sistema.txt