# Herramientas Avanzadas de Procesamiento y Shell Scripting
## Guía de Laboratorio (4 horas)
### Introducción
Este laboratorio está diseñado para proporcionar una experiencia práctica en el uso avanzado de herramientas de procesamiento de texto y shell scripting en entornos Unix/Linux. A lo largo de la sesión, los estudiantes progresarán desde el uso de filtros básicos hasta la creación de scripts complejos que integren múltiples herramientas y técnicas avanzadas.
### Objetivos de Aprendizaje
Al completar este laboratorio, los estudiantes serán capaces de:
1. Utilizar eficientemente las herramientas de procesamiento de texto de Unix
2. Crear scripts modulares y mantenibles en shell
3. Implementar soluciones para monitorización y análisis de sistemas
4. Aplicar buenas prácticas en el desarrollo de scripts
### Bloques del Laboratorio
El laboratorio se estructura en cuatro bloques que progresan desde conceptos básicos hasta técnicas avanzadas:
1. **Filtros Fundamentales y Procesamiento de Texto** (80 min): Herramientas básicas de Unix, expresiones regulares y procesamiento con AWK y jq.
2. **Procesamiento Avanzado de Datos** (45 min): Manejo de datos estructurados y generación de reportes.
3. **Monitorización y Análisis del Sistema** (45 min): Herramientas para supervisión de recursos y análisis de logs.
4. **Shell Scripting Avanzado** (80 min): Técnicas avanzadas de programación en shell, incluyendo gestión de secretos y manejo de errores.
## Bloque 1: Filtros Fundamentales y Procesamiento de Texto (80 min)
En este bloque exploraremos las herramientas fundamentales para el procesamiento de texto, comenzando con filtros básicos y progresando hacia herramientas más sofisticadas como AWK y jq. Los estudiantes aprenderán a combinar estas herramientas para crear pipelines eficientes de procesamiento de datos.
### 1.1 Introducción y Teoría (20 min)
#### Filosofía Unix de Filtros y Pipes
- Concepto de filtros: programas que leen de stdin y escriben a stdout
- Composición de comandos mediante pipes
- Ventajas del diseño modular
- Principio de responsabilidad única
#### Registros y Campos en la Shell
- Registros: Líneas individuales de texto que representan una unidad de información
- Campos: Partes de un registro separadas por delimitadores (espacios, tabs, comas, etc.)
- Delimitadores comunes:
- IFS (Internal Field Separator): Define el separador por defecto (espacio, tab, nueva línea)
- Delimitadores personalizados: comas (CSV), tabuladores (TSV), pipes (|), etc.
- Acceso a campos:
- $1, $2, etc.: Referencias a campos individuales
- $0: Línea completa
- NF: Número total de campos (en awk)
- Delimitador de registro (RS - Record Separator):
- Por defecto es el carácter de nueva línea (\n)
- Se puede modificar en herramientas como awk usando RS="delimitador"
- Permite procesar registros que ocupan múltiples líneas
- Ejemplos comunes:
- Párrafos (RS="\n\n")
- Registros XML (RS="</record>")
- Bloques delimitados por marcadores específicos
#### Filtros de Ordenación y Unicidad
1. **sort**: Ordenación versátil de texto
```bash
# Ordenación básica
sort archivo.txt
# Ordenación numérica
sort -n numeros.txt
# Ordenación por múltiples campos
sort -k2,2n -k3,3r datos.txt # -k START,END[tipo]: START es el campo inicial, END el campo final
# tipo: n=numérico, r=reverso, etc.
# Ej: -k2,2n ordena campo 2 numéricamente
# Se pueden encadenar múltiples -k para ordenar por varios campos
# Ordenación considerando el locale
LC_ALL=es_ES.UTF-8 sort -d nombres.txt
```
**Ejercicios**
- Ordena por orden alfabético las líneas del archivo `lorem.txt`. Hazlo también en orden inverso
- Ordena por orden numérico las líneas del archivo `integers.txt`.
- Ordena las líneas de `/etc/passwd` utilizando el UID como clave. Usa -t':' para especificar los dos puntos como delimitador de campo y -k3,3n para ordenar numéricamente por el tercer campo (UID):
```bash
sort -t':' -k3,3n /etc/passwd
```
2. **uniq**: Manejo de líneas duplicadas
```bash
# Eliminar duplicados consecutivos
sort datos.txt | uniq
# Contar ocurrencias
sort datos.txt | uniq -c
# Mostrar solo duplicados
sort datos.txt | uniq -d
```
**Ejercicios**
- Mostrar solo datos duplicados del archivo `ips.txt`
3. **paste**: Combinación de archivos
```bash
# Unir archivos lado a lado
paste nombres.txt telefonos.txt
# Usar delimitadores personalizados
paste -d',' archivo1.txt archivo2.txt
```
### 1.2 Filtros de Transformación y Búsqueda (20 min)
#### Manipulación de Texto
1. **cut**: Extracción de campos
```bash
# Extraer por delimitador
cut -d',' -f1,3 datos.csv
# Extraer por posición de caracteres
cut -c1-10 archivo.txt
```
2. **sed**: Editor de flujo
```bash
# Sustitución básica
sed 's/antiguo/nuevo/' archivo.txt
# Sustitución global en línea
sed 's/antiguo/nuevo/g' archivo.txt
# Operaciones múltiples
sed -e 's/uno/1/g' -e 's/dos/2/g' archivo.txt
```
3. **tr**: Transformación de caracteres
```bash
# Convertir mayúsculas a minúsculas
tr '[:upper:]' '[:lower:]' < archivo.txt
# Eliminar caracteres específicos
tr -d '\r' < archivo.dos > archivo.unix
```
#### Expresiones Regulares y grep
Las expresiones regulares (regex) son patrones de búsqueda que permiten encontrar y manipular texto de forma flexible y potente. Son una herramienta fundamental en el procesamiento de texto y se utilizan en múltiples herramientas como grep, sed, awk, y lenguajes de programación.
##### Elementos Básicos
- **Caracteres Literales**: Coinciden consigo mismos
- `casa` coincide exactamente con "casa"
- **Metacaracteres**: Caracteres especiales con significado
- `.` : Cualquier carácter excepto nueva línea
- `^` : Inicio de línea
- `$` : Fin de línea
- `*` : Cero o más repeticiones
- `+` : Una o más repeticiones
- `?` : Cero o una repetición
- `\` : Escape de metacaracteres
##### Clases de Caracteres
- **Conjuntos**: `[...]` Define un conjunto de caracteres
- `[aeiou]` : Cualquier vocal
- `[0-9]` : Cualquier dígito
- `[A-Za-z]` : Cualquier letra
- `[^0-9]` : Cualquier carácter que NO sea un dígito
##### Cuantificadores
- `{n}` : Exactamente n veces
- `{n,}` : n o más veces
- `{n,m}` : Entre n y m veces
- `*` : 0 o más veces (equivalente a {0,})
- `+` : 1 o más veces (equivalente a {1,})
- `?` : 0 o 1 vez (equivalente a {0,1})
##### Ejemplos Prácticos
1. **Búsqueda de DNIs**:
```regex
^[0-9]{8}[A-Z]$
```
Encuentra DNIs españoles (8 dígitos + letra mayúscula)
2. **Direcciones Email**:
```regex
^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$
```
Patrón básico para emails
3. **Números de Teléfono Españoles**:
```regex
^[6789][0-9]{8}$
```
Móviles y fijos españoles
4. **Direcciones IP**:
```regex
^([0-9]{1,3}\.){3}[0-9]{1,3}$
```
Formato básico de IPv4
##### Uso con grep
```bash
# Buscar líneas que empiezan con número
grep '^[0-9]' archivo.txt
# Encontrar DNIs en un archivo
grep -E '^[0-9]{8}[A-Z]$' datos.txt
# Buscar emails
grep -E '[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}' contactos.txt
# Ignorar mayúsculas/minúsculas
grep -i 'patron' archivo.txt
```
##### Consejos Prácticos
1. **Escapado de Caracteres**:
- Usar `\` para caracteres especiales
- Ejemplo: `\.` para buscar un punto literal
2. **Agrupación**:
- `(...)` para agrupar expresiones
- Útil para repeticiones y alternativas
3. **Alternativas**:
- `|` para especificar alternativas
- Ejemplo: `gato|perro` coincide con "gato" o "perro"
4. **Límites de Palabra**:
- `\b` marca límite de palabra
- Ejemplo: `\bpala\b` encuentra "pala" pero no "palabra"
```bash
# Búsqueda básica
grep 'patrón' archivo.txt
# Contexto alrededor de coincidencias
grep -C2 'error' log.txt
# Expresiones regulares extendidas
grep -E '^[0-9]{3}-[0-9]{2}-[0-9]{4}' datos.txt
```
### 1.3 AWK para Procesamiento de Texto (25 min)
#### Conceptos Básicos de AWK
```awk
# Estructura básica
awk 'BEGIN { inicialización }
/patrón/ { acción }
END { finalización }'
# Variables especiales
# $0 - línea completa
# $1, $2, ... - campos
# NR - número de registro (línea)
# NF - número de campos
# FS - separador de campo (input)
# OFS - separador de campo (output)
```
#### Ejemplos Prácticos
```awk
# Suma de una columna
awk '{ sum += $3 } END { print "Total:", sum }' datos.txt
# Procesamiento condicional
awk '$3 > 1000 { print $1, $2 }' ventas.txt
# Estadísticas básicas
awk '
{ sum += $1; count++ }
END {
print "Promedio:", sum/count
print "Total líneas:", count
}
' numeros.txt
```
### 1.4 Procesamiento JSON con jq (15 min)
#### Operaciones con jq
Los siguientes ejemplos utilizan los archivos generados en la carpeta `samples/`:
- `registros.json`: Colección de registros
- `simple-record.json`: Ejemplo de un único registro
##### Consultas Básicas
```bash
# Ver estructura de un registro
jq '.' samples/simple-record.json
# Extraer un campo específico
jq '.nombre_completo' samples/simple-record.json
# Extraer múltiples campos
jq '{nombre: .nombre_completo, edad: .edad}' samples/simple-record.json
# Contar número de registros
jq 'length' samples/registros.json
```
##### Filtrado y Búsqueda
```bash
# Personas mayores de 30 años
jq '.[] | select(.edad > 30)' samples/registros.json
# Registros activos con puntuación alta
jq '.[] | select(.activo == true and .puntuacion > 8)' samples/registros.json
# Buscar por preferencia específica
jq '.[] | select(.preferencias | contains(["deportes"]))' samples/registros.json
# Personas con más de 3 preferencias
jq '.[] | select(.preferencias | length > 3)' samples/registros.json
```
##### Transformaciones
```bash
# Extraer solo DNI y nombre
jq '.[] | {dni, nombre: .nombre_completo}' samples/registros.json
# Convertir a formato CSV (DNI,Nombre,Edad)
jq -r '.[] | [.dni, .nombre_completo, .edad] | @csv' samples/registros.json
# Agrupar por rango de edad
jq 'group_by(.edad | . / 10 | floor * 10) | map({
rango: "\\(.[0].edad / 10 | floor * 10)-\\(.[0].edad / 10 | floor * 10 + 9)",
cantidad: length
})' samples/registros.json
```
##### Estadísticas
```bash
# Calcular promedio de edad
jq '[.[].edad] | add/length' samples/registros.json
# Distribución de preferencias
jq '[.[].preferencias[]] | reduce .[] as $pref ({};
.[$pref] += 1)' samples/registros.json
# Estadísticas de puntuación
jq '[.[].puntuacion] | {
promedio: (add/length),
min: min,
max: max
}' samples/registros.json
```
##### Filtros Compuestos
```bash
# Personas activas, mayores de 25, con puntuación > 7
jq '.[] | select(.activo and .edad > 25 and .puntuacion > 7) |
{nombre: .nombre_completo, edad, puntuacion}' samples/registros.json
# Top 5 personas con mayor puntuación
jq 'sort_by(.puntuacion) | reverse | .[0:5] |
map({nombre: .nombre_completo, puntuacion})' samples/registros.json
# Agrupar por estado activo/inactivo con estadísticas
jq 'group_by(.activo) | map({
estado: if .[0].activo then "activo" else "inactivo" end,
cantidad: length,
edad_promedio: ([.[].edad] | add/length),
puntuacion_promedio: ([.[].puntuacion] | add/length)
})' samples/registros.json
```
##### Manipulación de Datos
```bash
# Añadir campo calculado
jq '.[] | . + {
categoria: if .puntuacion >= 8 then "alta"
elif .puntuacion >= 5 then "media"
else "baja" end
}' samples/registros.json
# Formatear teléfonos con separadores
jq '.[] | . + {
telefono_formato: .telefono |
"\(.[0:3])-\(.[3:5])-\(.[5:7])-\(.[7:9])"
}' samples/registros.json
# Extraer apellidos del nombre completo
jq '.[] | . + {
apellidos: (.nombre_completo | split(", ")[0])
}' samples/registros.json
```
#### Construcción de JSON
La construcción de JSON en shell scripts se puede realizar de varias maneras, pero el uso de `jq` ofrece ventajas significativas sobre métodos más simples como `echo`. A continuación, exploraremos las diferentes técnicas y sus implicaciones.
##### Métodos de Construcción JSON
Existen dos métodos principales para crear JSON desde la línea de comandos:
1. **Usando echo (NO RECOMENDADO)**:
```bash
echo "{\"nombre\": \"$NOMBRE\"}"
```
2. **Usando jq (RECOMENDADO)**:
```bash
jq -n --arg nombre "$NOMBRE" '{"nombre": $nombre}'
```
##### Ventajas de usar jq -n con --arg
1. **Seguridad**:
- Previene inyección de JSON malicioso
- Escapa automáticamente caracteres especiales
- Maneja correctamente valores nulos y tipos de datos
2. **Validación**:
- Garantiza que el JSON generado es válido
- Detecta errores de sintaxis en tiempo de ejecución
- Formatea el JSON de manera consistente
##### Diferencias entre --arg y --argjson
La herramienta jq proporciona dos formas principales de pasar argumentos:
- **--arg**: Siempre trata el valor como string
- **--argjson**: Interpreta el valor como JSON (números, booleanos, null, arrays, objetos)
Veamos ejemplos de cada tipo de dato:
1. **Números**:
```bash
EDAD=25
# Con --arg (edad como string):
jq -n --arg edad "$EDAD" '{"edad": $edad}'
# Salida: {"edad": "25"}
# Con --argjson (edad como número):
jq -n --argjson edad "$EDAD" '{"edad": $edad}'
# Salida: {"edad": 25}
```
2. **Booleanos**:
```bash
ACTIVO=true
# Con --arg (booleano como string):
jq -n --arg activo "$ACTIVO" '{"activo": $activo}'
# Salida: {"activo": "true"}
# Con --argjson (booleano real):
jq -n --argjson activo "$ACTIVO" '{"activo": $activo}'
# Salida: {"activo": true}
```
3. **Arrays**:
```bash
NUMEROS='[1,2,3]'
# Con --arg (array como string):
jq -n --arg nums "$NUMEROS" '{"números": $nums}'
# Salida: {"números": "[1,2,3]"}
# Con --argjson (array real):
jq -n --argjson nums "$NUMEROS" '{"números": $nums}'
# Salida: {"números": [1,2,3]}
```
4. **Null**:
```bash
VALOR="null"
# Con --arg (null como string):
jq -n --arg val "$VALOR" '{"valor": $val}'
# Salida: {"valor": "null"}
# Con --argjson (null real):
jq -n --argjson val "$VALOR" '{"valor": $val}'
# Salida: {"valor": null}
```
##### Manejo de Caracteres Especiales
jq maneja automáticamente los caracteres especiales y el escapado:
```bash
DESCRIPCION="línea 1\nlínea 2"
# Con echo (problemas con \n):
echo "{\"descripcion\": \"$DESCRIPCION\"}"
# Con jq (maneja correctamente los saltos de línea):
jq -n --arg desc "$DESCRIPCION" '{"descripcion": $desc}'
```
##### Ejemplos Avanzados de Construcción JSON
1. **Objetos Anidados**:
```bash
# Crear objeto con múltiples niveles
jq -n --arg user "$USER" --arg home "$HOME" '{
"usuario": {
"nombre": $user,
"directorio": $home
}
}'
```
2. **Listas de Objetos**:
Hay varias formas de crear listas de objetos, cada una con sus ventajas:
a. **Método Directo**:
```bash
jq -n --arg nombre1 "Juan" --arg edad1 "30" \
--arg nombre2 "Ana" --arg edad2 "25" \
'[
{"nombre": $nombre1, "edad": $edad1},
{"nombre": $nombre2, "edad": $edad2}
]'
```
b. **Usando Arrays Split**:
```bash
# Útil cuando los datos vienen como strings separados
jq -n --arg nombres "Juan Ana María" --arg edades "30 25 28" '
($nombres | split(" ")) as $n |
($edades | split(" ")) as $e |
[ range(0; $n | length) as $i |
{
"nombre": $n[$i],
"edad": $e[$i]
}
]'
```
c. **Usando Bucle Bash**:
```bash
# Útil para construcción dinámica
nombres=("Juan" "Ana" "María")
edades=(30 25 28)
json_array=$(jq -n '[]')
for i in "${!nombres[@]}"; do
json_array=$(echo "$json_array" | jq --arg nombre "${nombres[$i]}" \
--arg edad "${edades[$i]}" \
'. + [{"nombre": $nombre, "edad": $edad}]')
done
echo "$json_array"
```
3. **Combinación de Archivos JSON**:
```bash
# Combinar dos archivos JSON
jq -s '.[0] * .[1]' archivo1.json archivo2.json
```
### Ejercicio Práctico Integrador (15 min)
Desarrollar un pipeline que:
1. Procese un log de servidor web
2. Extraiga campos específicos
3. Realice agregaciones
4. Genere un reporte en JSON
```bash
# Ejemplo de solución
cat access.log | \
awk '{print $1,$9,$10}' | \
sort | \
uniq -c | \
awk '{ print "{\"ip\":\""$2"\",\"status\":"$3",\"bytes\":"$4",\"count\":"$1"}"}' | \
jq -s '{ "accesos": . }'
```
## Bloque 2: Procesamiento Avanzado de Datos (45 min)
Este bloque profundiza en el manejo y transformación de datos estructurados, combinando diferentes herramientas de procesamiento para crear pipelines eficientes. Se pondrá especial énfasis en AWK para el análisis de datos tabulares y la generación de reportes personalizados.
### 2.1 Procesamiento de Datos Tabulares (15 min)
#### Formato CSV y TSV
- Estructura y convenciones
- Manejo de delimitadores especiales
- Procesamiento de encabezados
- Ejemplos prácticos con AWK:
```awk
# Convertir CSV a formato tabular
awk -F',' '{ printf "%-20s %-10s %-5s\n", $1, $2, $3 }' datos.csv
# Calcular totales por columna
awk -F',' 'NR>1 {sum+=$3} END {print "Total:", sum}' ventas.csv
```
##### Transformación de Formatos
- Conversión entre formatos (CSV ↔ JSON ↔ XML)
- Normalización de datos
- Manejo de caracteres especiales
- Validación de estructura
### 2.2 Generación de Reportes (15 min)
#### Reportes Estadísticos
- Cálculo de métricas básicas (media, mediana, moda)
- Agregaciones por grupo
- Formateo de salida
- Ejemplo:
```awk
# Reporte de ventas por región
awk -F',' '
NR>1 {
ventas[$1] += $2
count[$1]++
}
END {
print "Región\tTotal\tPromedio"
for (region in ventas) {
printf "%s\t%.2f\t%.2f\n",
region,
ventas[region],
ventas[region]/count[region]
}
}
' datos.csv
```
##### Visualización de Datos
- Histogramas en ASCII
- Gráficos de barras simples
- Tablas formateadas
- Ejemplo de histograma:
```awk
# Histograma de frecuencias
awk '{
count[$1]++
if(max < count[$1]) max = count[$1]
}
END {
for (item in count) {
printf "%-10s |", item
for (i=0; i<count[item]; i++) printf "#"
print " (" count[item] ")"
}
}' datos.txt
```
### 2.3 Integración de Herramientas (15 min)
#### Pipelines Avanzados
- Combinación de múltiples herramientas
- Procesamiento paralelo con xargs
- Manejo de errores en pipelines
- Ejemplo integrador:
```bash
# Pipeline para análisis de logs
cat access.log |
awk '{print $1,$9,$7}' |
sort |
uniq -c |
awk '$1>100' |
jq -R 'split(" ")|{count:.[0],ip:.[1],status:.[2],path:.[3]}' |
jq -s 'group_by(.status)|map({status:.[0].status,count:length})'
```
##### Casos de Uso Prácticos
- Análisis de logs de servidor
- Procesamiento de datos financieros
- Generación de reportes de rendimiento
- Ejemplo de reporte de rendimiento:
```bash
# Análisis de tiempos de respuesta
awk '
/response_time/ {
times[$1] = $3
count[$1]++
total[$1] += $3
if($3 > max[$1]) max[$1] = $3
if(!min[$1] || $3 < min[$1]) min[$1] = $3
}
END {
print "Endpoint\tAvg\tMin\tMax\tCount"
for (ep in times) {
printf "%s\t%.2f\t%.2f\t%.2f\t%d\n",
ep,
total[ep]/count[ep],
min[ep],
max[ep],
count[ep]
}
}
' server.log
```
#### Ejercicios Prácticos
1. Crear un pipeline que procese datos de ventas y genere un reporte por región y producto
2. Implementar un analizador de logs que identifique patrones de error y genere alertas
3. Desarrollar un script que transforme datos entre diferentes formatos manteniendo la integridad
## Bloque 3: Monitorización y Análisis del Sistema (45 min)
Este bloque se centra en técnicas y herramientas para la monitorización efectiva de recursos del sistema y análisis de logs. Los estudiantes aprenderán a utilizar herramientas estándar de Unix/Linux para supervisar el rendimiento del sistema y diagnosticar problemas.
### 3.1 Monitorización de Recursos del Sistema (20 min)
> **Nota sobre herramientas de monitorización**
>
> Algunas herramientas requieren instalación previa. Aquí se muestra cómo instalarlas según la distribución:
>
> En Debian/Ubuntu:
> ```bash
> # Herramientas básicas de monitorización
> sudo apt install sysstat # Incluye: sar, iostat, mpstat
> sudo apt install iotop # Monitor de I/O
> sudo apt install iftop # Monitor de tráfico de red
> sudo apt install nethogs # Monitor de ancho de banda por proceso
> ```
>
> En Red Hat/CentOS:
> ```bash
> sudo yum install sysstat
> sudo yum install iotop
> sudo yum install iftop
> sudo yum install nethogs
> ```
>
> Herramientas que suelen estar disponibles por defecto:
> - top, htop
> - ps, free
> - df, du
> - netstat (o ss)
> - vmstat
#### Monitorización de CPU
Comandos para monitorizar el uso de CPU:
1. **top -b -n 1**: Muestra una instantánea del estado del sistema
- `-b`: Modo batch (no interactivo)
- `-n 1`: Número de iteraciones (una sola vez)
Ejemplo de salida:
```
top - 14:23:36 up 3 days, 23:42, 1 user, load average: 0.52, 0.58, 0.59
Tasks: 180 total, 1 running, 179 sleeping, 0 stopped, 0 zombie
%Cpu(s): 5.9 us, 3.1 sy, 0.0 ni, 90.6 id, 0.3 wa, 0.0 hi, 0.1 si
MiB Mem: 15951.3 total, 9049.5 free, 4584.7 used, 2317.1 buff/cache
```
- Primera línea: Uptime y carga promedio
- Segunda línea: Resumen de procesos
- Tercera línea: Uso de CPU (us: usuario, sy: sistema, id: idle)
- Cuarta línea: Estado de la memoria
2. **vmstat 1**: Estadísticas de memoria virtual y CPU
- `1`: Actualiza cada segundo
Ejemplo de salida:
```
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 9049492 543008 1774084 0 0 0 3 1 2 6 3 91 0 0
```
- procs: r (ejecutando), b (bloqueados)
- memory: swpd (memoria swap), free (memoria libre)
- swap: si (swap in), so (swap out)
- io: bi (bloques in), bo (bloques out)
- cpu: us (usuario), sy (sistema), id (idle), wa (wait)
3. **mpstat -P ALL 1**: Estadísticas por CPU
- `-P ALL`: Muestra todos los procesadores
- `1`: Actualiza cada segundo
Ejemplo de salida:
```
Linux 5.15.0 (hostname) 14:23:36 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %idle
# Donde:
# CPU: Número de CPU (all para el total, 0,1,etc para cada procesador)
# %usr: Porcentaje de tiempo en modo usuario (aplicaciones)
# %nice: Porcentaje de tiempo en modo usuario con prioridad nice
# %sys: Porcentaje de tiempo en modo kernel (sistema)
# %iowait: Porcentaje de tiempo esperando por operaciones I/O
# %irq: Porcentaje de tiempo manejando interrupciones hardware
# %soft: Porcentaje de tiempo manejando interrupciones software
# %steal: Porcentaje de tiempo en espera involuntaria (virtualización)
# %guest: Porcentaje de tiempo ejecutando máquina virtual
# %idle: Porcentaje de tiempo en que la CPU está inactiva
14:23:36 all 5.89 0.00 3.11 0.31 0.00 0.10 0.00 0.00 90.59
14:23:36 0 6.12 0.00 3.06 0.41 0.00 0.20 0.00 0.00 90.21
14:23:36 1 5.67 0.00 3.16 0.20 0.00 0.00 0.00 0.00 90.97
```
- Muestra porcentajes de tiempo en diferentes estados para cada CPU
- Útil para detectar desbalances entre cores
4. **ps aux --sort=-%cpu**: Procesos ordenados por CPU
- `aux`: Formato detallado
- `--sort=-%cpu`: Ordena por CPU descendente
Ejemplo de salida:
```
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
user1 12345 8.5 2.1 654208 174504 ? Sl 13:25 0:45 firefox
user1 23456 6.2 1.5 542168 123456 ? Rl 14:01 0:32 chrome
```
- %CPU: Porcentaje de CPU usado
- %MEM: Porcentaje de memoria
- VSZ: Memoria virtual
- RSS: Memoria física real
5. **uptime**: Tiempo de funcionamiento y carga
Ejemplo de salida:
```
14:23:36 up 3 days, 23:42, 1 user, load average: 0.52, 0.58, 0.59
```
- Tiempo actual
- Tiempo de funcionamiento
- Número de usuarios
- Carga promedio (1, 5, 15 minutos)
6. **cat /proc/loadavg**: Carga del sistema en formato raw
Ejemplo de salida:
```
0.52 0.58 0.59 1/780 12345
```
- Tres primeros números: carga promedio 1, 5, 15 minutos
- 1/780: Procesos ejecutándose/total
- 12345: Último PID asignado
##### Monitorización de Memoria
Comandos para monitorizar el uso de memoria:
- `free -h`: Muestra uso de memoria y swap
- `-h`: Formato human-readable (GB, MB, etc.)
- `vmstat -s`: Estadísticas detalladas de memoria virtual
- `-s`: Muestra estadísticas en modo tabla
- `ps aux --sort=-%mem`: Lista procesos por uso de memoria
- `--sort=-%mem`: Ordena por uso de memoria descendente
- `top -b -n 1 -o %MEM`: Procesos ordenados por uso de memoria
- `-o %MEM`: Ordena por porcentaje de memoria
- `pmap -x PID`: Mapa de memoria de un proceso
- `-x`: Formato extendido con detalles
- Muestra RSS, tamaño y permisos por región
```bash
# Visión general del uso de memoria
free -h
vmstat -s
# Uso de memoria por proceso (incluye RSS)
ps aux --sort=-%mem | head -n 10
top -b -n 1 -o %MEM
# Memoria detallada de un proceso específico
pmap -x PID
cat /proc/PID/status
```
##### Monitorización de Disco
Comandos para monitorizar almacenamiento:
- `df -h`: Uso de espacio en sistemas de archivos
- `-h`: Formato human-readable
- `-i`: Muestra información de inodos
- `du -sh`: Uso de espacio por directorio
- `-s`: Summarize (total por directorio)
- `-h`: Formato human-readable
- `iostat -x 1`: Estadísticas de E/S
- `-x`: Modo extendido con más métricas
- `1`: Actualiza cada segundo
- `iotop -b -n 1`: Monitor de E/S por proceso
- `-b`: Modo batch
- `-n 1`: Una iteración
```bash
# Espacio en disco
df -h
df -i # Inodos
# Uso de disco por directorio
du -sh /*
du -sh /var/log/*
# I/O en tiempo real
iostat -x 1
iotop -b -n 1
```
##### Monitorización de Red
Comandos básicos para monitorizar red (disponibles por defecto):
1. **netstat -i**: Estado de interfaces
- `-i`: Muestra estadísticas de interfaz
Ejemplo de salida:
```
Iface MTU RX-OK RX-ERR RX-DRP RX-OVR TX-OK TX-ERR TX-DRP TX-OVR Flg
eth0 1500 158320 0 0 0 108959 0 0 0 BMRU
lo 65536 8166 0 0 0 8166 0 0 0 LRU
```
- MTU: Unidad máxima de transmisión
- RX: Paquetes recibidos (OK, errores, descartados, overflow)
- TX: Paquetes transmitidos (OK, errores, descartados, overflow)
- Flg: Flags de la interfaz (B:broadcast, M:multicast, R:running, U:up)
2. **netstat -tuln**: Puertos en escucha
- `-t`: TCP
- `-u`: UDP
- `-l`: Solo puertos en escucha
- `-n`: No resolver nombres
Ejemplo de salida:
```
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN
tcp6 0 0 :::80 :::* LISTEN
udp 0 0 127.0.0.1:53 0.0.0.0:*
```
- Proto: Protocolo (tcp/udp)
- Recv-Q/Send-Q: Bytes en cola de recepción/envío
- Local/Foreign Address: Direcciones y puertos locales/remotos
- State: Estado de la conexión (para TCP)
3. **ss**: Versión moderna de netstat (reemplazo recomendado)
```bash
# Mostrar todas las conexiones TCP
ss -ta
# Mostrar estadísticas
ss -s
```
Ejemplo de salida ss -s:
```
Total: 584
TCP: 47 (estab 36, closed 0, orphaned 0, timewait 0)
Transport Total IP IPv6
RAW 0 0 0
UDP 17 12 5
TCP 47 36 11
```
Herramientas avanzadas (requieren instalación):
4. **iftop** (opcional): Monitor de tráfico en tiempo real
```bash
# Instalar: sudo apt install iftop
iftop -n # -n: no resolver nombres
```
5. **nethogs** (opcional): Monitor de ancho de banda por proceso
```bash
# Instalar: sudo apt install nethogs
nethogs eth0
```
Comandos básicos para diagnóstico de red:
```bash
# Ver interfaces y su estado
ip link show
# Ver direcciones IP
ip addr show
# Ver tabla de rutas
ip route show
# Estadísticas de interfaces
netstat -i
# Puertos en escucha
netstat -tuln
# o
ss -tuln
```
#### 3.2 Análisis de Logs del Sistema (15 min)
##### Logs Críticos del Sistema
```bash
# Mensajes del sistema
tail -f /var/log/syslog
tail -f /var/log/messages
# Logs de autenticación
tail -f /var/log/auth.log
# Logs del kernel
dmesg | tail
journalctl -k -f
```
##### Filtrado Básico de Logs
```bash
# Buscar errores
grep -i error /var/log/syslog
grep -i failed /var/log/auth.log
# Eventos recientes
journalctl -n 50 --no-pager
```
#### 3.3 Scripts de Monitorización (10 min)
##### Script Básico de Monitorización
```bash
#!/bin/bash
# Monitor de recursos básico
# CPU
echo "=== CPU ==="
top -b -n 1 | head -n 3
# Memoria
echo -e "\n=== Memoria ==="
free -h | head -n 2
# Disco
echo -e "\n=== Disco ==="
df -h | head -n 6
# Red
echo -e "\n=== Red ==="
netstat -i | head -n 3
```
##### Variables de Entorno para Monitorización
```bash
# Configuración de umbrales
DISK_THRESHOLD=${DISK_THRESHOLD:-90}
MEM_THRESHOLD=${MEM_THRESHOLD:-80}
CPU_THRESHOLD=${CPU_THRESHOLD:-75}
```
#### Ejercicios Prácticos
1. Crear un script que monitorice y alerte cuando el uso de CPU supere un umbral
2. Implementar un monitor de espacio en disco que identifique directorios de mayor crecimiento
3. Desarrollar un script que recopile estadísticas de red y genere un reporte horario
## Bloque 4: Shell Scripting Avanzado (80 min)
Este bloque final integra conceptos avanzados de shell scripting, con especial énfasis en el manejo de arrays, expansión de variables y gestión segura de secretos. Los estudiantes aprenderán a crear scripts robustos y seguros utilizando las características avanzadas de la shell.
### 4.1 Fundamentos de Arrays y Variables (25 min)
#### Arrays en Bash
1. **Declaración y Asignación**
```bash
# Array indexado
frutas=("manzana" "pera" "naranja")
# Array asociativo
declare -A usuarios
usuarios[admin]="root"
usuarios[web]="www-data"
```
2. **Operaciones con Arrays**
```bash
# Añadir elementos
frutas+=("plátano")
# Acceder a elementos
echo "${frutas[0]}" # Primer elemento
echo "${frutas[-1]}" # Último elemento
echo "${frutas[@]}" # Todos los elementos
echo "${#frutas[@]}" # Número de elementos
echo "${!frutas[@]}" # Índices del array
# Slicing
echo "${frutas[@]:1:2}" # 2 elementos desde índice 1
```
3. **Iteración sobre Arrays**
```bash
# Iterar sobre valores
for fruta in "${frutas[@]}"; do
echo "Procesando: $fruta"
done
# Iterar sobre índices
for i in "${!frutas[@]}"; do
echo "Índice $i: ${frutas[$i]}"
done
```
##### Expansión de Variables Avanzada
1. **Valores por Defecto**
```bash
# Si variable no existe o está vacía
echo "${PUERTO:-8080}" # Usa 8080 como valor por defecto
echo "${USUARIO:=anonymous}" # Asigna y usa valor por defecto
# Manejo de errores
echo "${CONFIG:?'Archivo de configuración no especificado'}"
# Valor alternativo si existe
echo "${DEBUG:+'-v'}" # Usa -v solo si DEBUG está definida
```
2. **Manipulación de Strings**
```bash
# Substrings
texto="Hola Mundo"
echo "${texto:0:4}" # "Hola"
echo "${texto:5}" # "Mundo"
# Patrones
archivo="script.test.sh"
echo "${archivo#*.}" # "test.sh" (elimina hasta primer punto)
echo "${archivo##*.}" # "sh" (elimina hasta último punto)
echo "${archivo%.*}" # "script.test" (elimina desde último punto)
echo "${archivo%%.*}" # "script" (elimina desde primer punto)
```
3. **Sustitución de Patrones**
```bash
# Reemplazar primera ocurrencia
echo "${texto/Mundo/Amigo}" # "Hola Amigo"
# Reemplazar todas las ocurrencias
echo "${texto//o/0}" # "H0la Mund0"
# Mayúsculas/minúsculas
echo "${texto^^}" # "HOLA MUNDO"
echo "${texto,,}" # "hola mundo"
```
### 4.2 Gestión de Secretos y Variables de Entorno (20 min)
#### Codificación y Decodificación base64
```bash
# Codificar secretos
SECRET=$(echo -n "mi_contraseña_secreta" | base64)
echo "Secreto codificado: $SECRET"
# Decodificar
echo "$SECRET" | base64 -d
# Manejo de múltiples líneas
CONFIG=$(cat <<EOF | base64
usuario=admin
password=secreto
host=localhost
EOF
)
```
#### Cifrado de Secretos con OpenSSL
1. **Cifrado Simétrico Básico**
```bash
# Generar clave segura
PASSPHRASE=$(openssl rand -base64 32)
# Cifrar un secreto
echo "secreto_importante" | openssl enc -aes-256-cbc -salt -pbkdf2 \
-out secreto.enc -pass pass:"$PASSPHRASE"
# Descifrar
openssl enc -aes-256-cbc -d -salt -pbkdf2 \
-in secreto.enc -pass pass:"$PASSPHRASE"
```
2. **Gestión de Archivos de Configuración**
```bash
#!/bin/bash
# Función para cifrar configuración
encrypt_config() {
local config_file="$1"
local key_file="$2"
if [[ ! -f "$config_file" ]]; then
echo "Error: Archivo de configuración no encontrado"
return 1
}
openssl enc -aes-256-cbc -salt -pbkdf2 \
-in "$config_file" \
-out "${config_file}.enc" \
-pass file:"$key_file"
# Eliminar archivo original de forma segura
shred -u "$config_file"
}
# Función para descifrar configuración
decrypt_config() {
local encrypted_file="$1"
local key_file="$2"
local temp_file=$(mktemp)
openssl enc -aes-256-cbc -d -salt -pbkdf2 \
-in "$encrypted_file" \
-out "$temp_file" \
-pass file:"$key_file"
# Cargar configuración
source "$temp_file"
# Limpiar
shred -u "$temp_file"
}
```
#### Mejores Prácticas de Seguridad
1. **Gestión de Variables de Entorno**
```bash
# Archivo .env.template (versionar esto)
DB_HOST=localhost
DB_PORT=5432
DB_USER=
DB_PASS=
# Script para cargar variables
load_env() {
if [[ -f .env ]]; then
set -a
source .env
set +a
else
echo "Error: Archivo .env no encontrado"
exit 1
fi
}
```
2. **Validación de Secretos**
```bash
validate_secrets() {
local required_vars=("DB_USER" "DB_PASS" "API_KEY")
local missing=0
for var in "${required_vars[@]}"; do
if [[ -z "${!var}" ]]; then
echo "Error: Variable $var no definida"
missing=1
fi
done
return $missing
}
```
### 4.3 Funciones y Modularización (20 min)
#### Diseño de Funciones
```bash
# Función con validación de argumentos
get_user_data() {
local user_id="$1"
local field="${2:-name}" # valor por defecto
if [[ -z "$user_id" ]]; then
echo "Error: user_id requerido" >&2
return 1
fi
# Usar variable local para el resultado
local result
result=$(grep "^$user_id:" /etc/passwd)
case "$field" in
name) echo "$result" | cut -d: -f1 ;;
home) echo "$result" | cut -d: -f6 ;;
shell) echo "$result" | cut -d: -f7 ;;
*) echo "Campo no válido: $field" >&2; return 1 ;;
esac
}
```
#### Bibliotecas de Funciones
```bash
# lib/logger.sh
log() {
local level="$1"
shift
local message="$*"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
case "$level" in
INFO|WARNING|ERROR|DEBUG)
echo "[$timestamp] $level: $message" >&2
;;
*)
echo "Nivel de log no válido: $level" >&2
return 1
;;
esac
}
# lib/config.sh
load_config() {
local config_file="$1"
if [[ ! -f "$config_file" ]]; then
log ERROR "Archivo de configuración no encontrado: $config_file"
return 1
fi
source "$config_file"
}
```
### 4.4 Uso de exec (15 min)
#### Fundamentos de exec
`exec` es una característica poderosa en bash que permite:
- Reemplazar el proceso actual con un nuevo proceso
- Redirigir descriptores de archivo
- Ejecutar comandos con mayor eficiencia
1. **Reemplazo de Proceso**
```bash
# El script termina aquí, reemplazado por date
exec date
echo "Esto nunca se ejecuta"
# Útil para wrappers y proxies
#!/bin/bash
# wrapper.sh
setup_environment() {
export PATH="/custom/path:$PATH"
export CUSTOM_VAR="value"
}
setup_environment
exec "$@" # Reemplaza el wrapper con el comando proporcionado
```
2. **Redirección de Descriptores de Archivo**
```bash
# Redirigir stdout a un archivo para todo el script
exec 1>log.txt
# Redirigir stderr a stdout
exec 2>&1
# Crear nuevo descriptor de archivo para lectura
exec 3<input.txt
read -u 3 line
# Crear nuevo descriptor de archivo para escritura
exec 4>output.txt
echo "log entry" >&4
# Cerrar un descriptor de archivo
exec 4>&-
```
3. **Uso en Scripts de Logging**
```bash
#!/bin/bash
# Configurar logging
setup_logging() {
local log_file="$1"
# Guardar stdout original
exec 3>&1
# Redirigir stdout y stderr al archivo de log
exec 1>"$log_file" 2>&1
# Ahora podemos usar fd 3 para output real
echo "Starting script" >&3
}
# Restaurar stdout
cleanup_logging() {
# Restaurar stdout y stderr
exec 1>&3 2>&1
# Cerrar fd temporal
exec 3>&-
}
```
4. **Patrones Comunes**
```bash
# Ejecutar en nuevo shell sin afectar el actual
(
# Estas redirecciones solo afectan al subshell
exec 1>log.txt 2>error.log
./script.sh
)
# Capturar tanto stdout como stderr separadamente
{
exec 3>&1 # Guardar stdout actual
exec 4>&2 # Guardar stderr actual
exec 1>stdout.log # Nuevo stdout
exec 2>stderr.log # Nuevo stderr
# Código aquí...
exec 1>&3 # Restaurar stdout
exec 2>&4 # Restaurar stderr
exec 3>&- # Cerrar fd temporal
exec 4>&- # Cerrar fd temporal
}
```
5. **Mejores Prácticas**
```bash
#!/bin/bash
# Declarar descriptores de archivo como readonly
declare -r LOG_FD=3
declare -r ERR_FD=4
# Función para inicializar descriptores
setup_fd() {
# Verificar que los fd no estén en uso
if [ -e /dev/fd/$LOG_FD ] || [ -e /dev/fd/$ERR_FD ]; then
echo "Error: Descriptores de archivo ya en uso" >&2
exit 1
fi
# Abrir descriptores
exec "$LOG_FD">app.log
exec "$ERR_FD">error.log
}
# Función para limpiar descriptores
cleanup_fd() {
# Cerrar descriptores abiertos
exec "$LOG_FD">&-
exec "$ERR_FD">&-
}
# Asegurar limpieza al salir
trap cleanup_fd EXIT
```
#### Casos de Uso Avanzados
1. **Daemon Script**
```bash
#!/bin/bash
daemonize() {
# Redirigir descriptores estándar
exec 1>/dev/null
exec 2>/dev/null
exec 0</dev/null
# Ejecutar en background
(
# Desacoplar del terminal
cd /
umask 0
setsid "$@" &
)
}
# Uso
daemonize ./servicio.sh
```
2. **Rotación de Logs**
```bash
#!/bin/bash
rotate_logs() {
local log_file="$1"
local max_size=$((10*1024*1024)) # 10MB
if [[ -f "$log_file" && $(stat -f%z "$log_file") -gt $max_size ]]; then
# Cerrar descriptor actual
exec 3>&-
# Rotar archivo
mv "$log_file" "${log_file}.old"
# Reabrir descriptor
exec 3>"$log_file"
fi
}
```
### 4.5 Manejo de Errores y Depuración (15 min)
#### Control de Errores
```bash
#!/bin/bash
set -euo pipefail
# Trap para cleanup
cleanup() {
local exit_code=$?
log INFO "Limpiando recursos temporales..."
rm -f /tmp/temp.*
exit $exit_code
}
trap cleanup EXIT
# Manejo de errores personalizado
error_handler() {
local line_no=$1
local error_code=$2
log ERROR "Error en línea $line_no (código: $error_code)"
}
trap 'error_handler ${LINENO} $?' ERR
```
#### Logging y Depuración
```bash
# Niveles de debug
DEBUG=${DEBUG:-0}
debug() {
if ((DEBUG >= 1)); then
log DEBUG "$*"
fi
}
trace() {
if ((DEBUG >= 2)); then
log TRACE "$*"
fi
}
# Uso
debug "Procesando archivo: $file"
trace "Variables: $(set | grep '^[A-Z]')"
```
#### Ejercicios Prácticos
1. Crear una biblioteca de funciones para gestión de configuración que:
- Soporte múltiples formatos (env, json)
- Valide valores requeridos
- Maneje secretos de forma segura
2. Implementar un script que procese un array de usuarios y:
- Valide formato de datos
- Aplique transformaciones
- Genere reportes
- Maneje errores apropiadamente
3. Desarrollar un sistema de logging que:
- Soporte múltiples niveles
- Rote archivos de log
- Incluya información de contexto
- Permita filtrado por severidad
### Metodología
- Cada bloque combina explicación teórica con ejercicios prácticos
- Los ejercicios están diseñados para ser incrementales en complejidad
- Se proporcionarán datos de ejemplo para las prácticas
- Los estudiantes trabajarán con casos de uso realistas
- Se fomentará la experimentación y la resolución de problemas
### Requisitos Previos
Los estudiantes deben tener:
- Conocimientos básicos de línea de comandos Unix/Linux
- Experiencia básica en scripting shell
- Familiaridad con conceptos básicos de programación
- Un entorno Linux/Unix con las herramientas estándar instaladas