2  Dominando la terminal para Big Data

Comandos, filtros, tuberías y un mini-proyecto de logs sobre Linux

big-data
linux
bash
terminal
Autor/a

Wilder Ramírez Delgado

Fecha de publicación

15 de octubre de 2026

En el capítulo anterior dejaste Ubuntu listo y aprendiste a orientarte: qué es el kernel, dónde vive cada cosa en el árbol de directorios y qué hacen HOME y PATH. Ya tienes el territorio y la ventana. Falta el lenguaje: la forma en que un ingeniero de datos le habla a una máquina Linux todos los días. En este capítulo vas a aprender a leer y escribir comandos con intención, a encadenarlos con tuberías y a responder preguntas reales sobre un archivo de logs sin abrir una hoja de cálculo. Al final verás que el famoso wordcount de MapReduce ya cabe en una línea de tu terminal.


Tiempo estimado de lectura: 45 minutos

Nivel: Básico

Requisitos:

Tecnologías utilizadas:


Cómo trabajar este capítulo

Todos los bloques son de Bash y se copian a la terminal de Ubuntu, dentro de ~/bigdata_linux_lab. Debajo de cada uno verás la salida esperada: la obtuvimos ejecutando exactamente esos comandos en Ubuntu 24.04. Si tu salida difiere en fechas, usuario o tamaño de un archivo, es normal; si difiere en los conteos, revisa que copiaste el bloque completo.

Ningún bloque usa sudo ni borra nada fuera del laboratorio.


1. Por qué la terminal sigue siendo la herramienta principal

Un clúster de Big Data no se administra con el ratón. Los nodos no tienen escritorio, los logs pesan gigabytes y la pregunta urgente casi nunca es “¿cómo se ve?”, sino “¿qué pasó a las 3:12 a. m. y en qué nodo?”. La terminal responde esas preguntas con tres ventajas que ninguna interfaz gráfica reúne a la vez:

  • Funciona igual en tu portátil y en un nodo remoto. Lo que aprendes aquí lo repites por SSH o dentro de un contenedor Docker sin cambiar una letra.
  • Se compone. Cada herramienta hace una sola cosa y las unes como piezas de Lego. Esa es la filosofía Unix, y es exactamente la idea que MapReduce llevó a cientos de máquinas.
  • Se automatiza. Lo que hoy escribes a mano mañana es un script que corre cada noche.

En producción… cuando un trabajo de Spark falla, lo primero que hace un ingeniero no es abrir un dashboard: es buscar con grep en los logs del driver y de los executors. Este capítulo te prepara para ese momento.


2. Preparar los datos de práctica

Vamos a trabajar con dos archivos pequeños, para que puedas comprobar cada resultado a ojo:

  • raw/accesos.log: un registro de peticiones a una API, con 24 líneas en dos días.
  • raw/texto.txt: el mismo texto de cuatro líneas que usarán los capítulos de YARN, MapReduce y Spark.

Copia el bloque completo, incluida la línea final EOF:

cd ~/bigdata_linux_lab
cat > raw/accesos.log <<'EOF'
2026-10-01 08:00:01 10.0.0.12 GET /api/ventas 200 5120 34
2026-10-01 08:00:03 10.0.0.15 GET /api/clientes 200 2048 21
2026-10-01 08:01:10 10.0.0.12 POST /api/ventas 201 256 88
2026-10-01 08:02:45 10.0.0.20 GET /api/inventario 500 128 1203
2026-10-01 08:03:00 10.0.0.15 GET /api/ventas 200 4096 30
2026-10-01 08:05:12 10.0.0.31 GET /login 200 1024 12
2026-10-01 08:05:40 10.0.0.31 POST /login 401 64 15
2026-10-01 08:06:02 10.0.0.31 POST /login 200 512 18
2026-10-01 08:10:33 10.0.0.12 GET /api/inventario 200 3072 95
2026-10-01 08:12:09 10.0.0.20 GET /api/inventario 503 128 3001
2026-10-01 08:15:55 10.0.0.15 GET /api/clientes 404 64 9
2026-10-01 08:20:20 10.0.0.12 GET /api/ventas 200 5120 41
2026-10-02 09:00:05 10.0.0.15 GET /api/ventas 200 6144 37
2026-10-02 09:00:30 10.0.0.44 GET /login 200 1024 11
2026-10-02 09:01:02 10.0.0.44 POST /login 200 512 16
2026-10-02 09:03:47 10.0.0.12 GET /api/inventario 500 128 1450
2026-10-02 09:04:10 10.0.0.20 GET /api/clientes 200 2048 25
2026-10-02 09:07:31 10.0.0.12 POST /api/ventas 201 256 92
2026-10-02 09:09:59 10.0.0.15 GET /api/ventas 200 4096 33
2026-10-02 09:12:14 10.0.0.44 GET /api/inventario 200 3072 101
2026-10-02 09:15:00 10.0.0.20 GET /api/ventas 200 5120 39
2026-10-02 09:18:22 10.0.0.31 GET /api/clientes 200 2048 23
2026-10-02 09:20:48 10.0.0.12 GET /api/inventario 200 3072 88
2026-10-02 09:25:16 10.0.0.15 DELETE /api/clientes 403 64 8
EOF
printf 'hadoop hdfs yarn\nhadoop mapreduce yarn\nspark hadoop datos\nyarn datos datos\n' > raw/texto.txt

Cada línea de accesos.log tiene ocho campos separados por un espacio:

Campo Ejemplo Significado
1 2026-10-01 Fecha
2 08:00:01 Hora
3 10.0.0.12 Dirección IP del cliente
4 GET Método HTTP
5 /api/ventas Ruta pedida
6 200 Código de estado (2xx éxito, 4xx error del cliente, 5xx error del servidor)
7 5120 Bytes enviados
8 34 Tiempo de respuesta en milisegundos

Ten esta tabla a mano: casi todo el capítulo consiste en preguntarle cosas a esas columnas.

¿Qué hizo cat > archivo <<'EOF'? Es un here document: todo lo que escribes entre la primera línea y EOF se envía a cat, y > lo guarda en el archivo. Las comillas en 'EOF' impiden que Bash interprete símbolos como $ dentro del texto. Es la forma estándar de crear archivos de configuración desde la terminal; los laboratorios de HDFS y YARN la usan para escribir los XML del clúster.


3. Anatomía de un comando

Todo comando tiene la misma forma:

comando  [opciones]  [argumentos]
Parte Qué es Ejemplo en ls -l -h raw
Comando El programa que se ejecuta ls
Opciones (flags) Modifican cómo trabaja el programa; empiezan por - (corta) o -- (larga) -l (formato largo), -h (tamaños legibles)
Argumentos Sobre qué trabaja raw

Las opciones cortas se pueden combinar: ls -l -h raw y ls -lh raw son lo mismo.

ls -lh raw

Salida esperada (usuario y fecha serán los tuyos):

total 8.0K
-rw-r--r-- 1 ana ana 1.4K Oct 14 20:59 accesos.log
-rw-r--r-- 1 ana ana   75 Oct 14 20:59 texto.txt

Leída de izquierda a derecha: permisos, número de enlaces, dueño, grupo, tamaño, fecha de modificación y nombre. Sin -h, el tamaño aparece en bytes (1401).

Cómo pedir ayuda sin salir de la terminal

Comando Para qué
ls --help Resumen rápido de las opciones de un programa
man ls Manual completo (se sale con q, se busca con /palabra)
type ls Dice si un comando es un programa, un alias o parte del propio Bash
which grep Dice en qué carpeta del PATH está el programa
type cd
which grep

Salida esperada:

cd is a shell builtin
/usr/bin/grep

cd no es un programa aparte: es parte de Bash (builtin), porque tiene que cambiar la carpeta de la propia shell. Si pruebas type ls, en Ubuntu verás ls is aliased to `ls --color=auto': un alias es un atajo que la distribución define para que ls muestre colores.

Si man no existe: algunas instalaciones mínimas de Ubuntu traen los manuales recortados. --help funciona siempre; para recuperar los manuales, sudo unminimize (opcional, tarda varios minutos).

Comillas: cuándo Bash interpreta y cuándo no

Bash separa los argumentos por espacios y sustituye variables como $HOME antes de ejecutar el comando. Las comillas controlan ese comportamiento:

echo "Mi laboratorio está en $HOME/bigdata_linux_lab"
echo 'Mi laboratorio está en $HOME/bigdata_linux_lab'

Salida esperada (con tu usuario):

Mi laboratorio está en /home/ana/bigdata_linux_lab
Mi laboratorio está en $HOME/bigdata_linux_lab
Comillas Qué hace Cuándo usarlas
Dobles "..." Agrupan el texto en un solo argumento y sustituyen variables Rutas con espacios que contienen variables: "$HOME/mis datos"
Simples '...' Agrupan el texto y no sustituyen nada Patrones de awk o grep que llevan $ o espacios

Error común: escribir cd mis datos cuando la carpeta se llama mis datos. Bash entiende dos argumentos. La forma correcta es cd "mis datos". En Big Data la regla práctica es más simple: no uses espacios en nombres de archivos o carpetas.

Comodines y llaves

Antes de ejecutar un comando, Bash expande algunos símbolos especiales:

Símbolo Significa Ejemplo Se expande a
* Cualquier texto raw/*.log raw/accesos.log
? Exactamente un carácter dia?.csv dia1.csv dia2.csv
[...] Uno de los caracteres indicados dia[12].csv dia1.csv dia2.csv
{a,b} Cada alternativa, exista o no clean/{2026-10-01,2026-10-02} clean/2026-10-01 clean/2026-10-02

Las llaves son muy útiles para crear estructuras particionadas por fecha, como las que verás en HDFS:

mkdir -p clean/{2026-10-01,2026-10-02}
ls clean
rmdir clean/2026-10-01 clean/2026-10-02

Salida esperada:

2026-10-01
2026-10-02

rmdir borra solo carpetas vacías: es la forma segura de deshacer la prueba.

Historial y autocompletado

Dos hábitos que ahorran horas:

  • Tab completa nombres de comandos, archivos y carpetas. Si hay varias opciones, pulsa Tab dos veces para verlas. Escribir ra + Tab produce raw/.
  • Flecha arriba recupera el comando anterior. Ctrl+R busca en el historial: escribe grep y verás el último comando que lo contenía; pulsa Ctrl+R otra vez para ir más atrás.

history muestra la lista numerada de lo que has escrito. Es tu bitácora involuntaria.


4. Leer archivos sin abrir un editor

Comando Qué hace Cuándo usarlo
cat archivo Imprime el archivo completo Archivos cortos
less archivo Lo abre paginado (q para salir, /texto para buscar) Archivos largos
head -n 5 archivo Primeras 5 líneas Ver el formato de un archivo
tail -n 5 archivo Últimas 5 líneas Ver lo más reciente de un log
tail -f archivo Sigue mostrando las líneas nuevas (Ctrl+C para salir) Observar un log mientras un servicio corre
wc archivo Cuenta líneas, palabras y bytes Tamaño de un archivo en registros
head -n 3 raw/accesos.log
tail -n 2 raw/accesos.log
wc -l raw/accesos.log
wc raw/texto.txt

Salida esperada:

2026-10-01 08:00:01 10.0.0.12 GET /api/ventas 200 5120 34
2026-10-01 08:00:03 10.0.0.15 GET /api/clientes 200 2048 21
2026-10-01 08:01:10 10.0.0.12 POST /api/ventas 201 256 88
2026-10-02 09:20:48 10.0.0.12 GET /api/inventario 200 3072 88
2026-10-02 09:25:16 10.0.0.15 DELETE /api/clientes 403 64 8
24 raw/accesos.log
 4 12 75 raw/texto.txt

El texto tiene 4 líneas, 12 palabras y 75 bytes. Recuerda el 12: es el total que deberá dar el wordcount.

En producción… nunca hagas cat de un log de 20 GB: llenarás la pantalla durante minutos. Empieza siempre con head, tail o wc -l para saber a qué te enfrentas.


5. Flujos estándar y redirecciones

Todo programa de Linux nace con tres canales abiertos:

Canal Número Por defecto va a Para qué
Entrada estándar (stdin) 0 El teclado Lo que el programa lee
Salida estándar (stdout) 1 La pantalla El resultado
Error estándar (stderr) 2 La pantalla Los mensajes de error

Separar resultado y errores es una decisión de diseño valiosa: puedes guardar uno y descartar el otro. Las redirecciones cambian adónde va cada canal:

Redirección Qué hace
> archivo Envía stdout a un archivo, reemplazando su contenido
>> archivo Envía stdout al final del archivo (agrega)
2> archivo Envía stderr a un archivo
> archivo 2>&1 Envía stdout y stderr al mismo archivo
< archivo Usa el archivo como stdin
> /dev/null Descarta la salida (un “agujero negro”)

Provoca un error a propósito y redirígelo:

ls /no-existe
ls /no-existe 2> logs/errores.txt
cat logs/errores.txt

Salida esperada:

ls: cannot access '/no-existe': No such file or directory
ls: cannot access '/no-existe': No such file or directory

La primera línea la imprimió el primer ls en pantalla. El segundo ls no mostró nada: su error fue al archivo, y cat lo leyó.

Ahora junta resultado y error en un solo archivo, como hace un servicio que escribe su log:

ls raw /no-existe > out/listado.txt 2>&1
cat out/listado.txt

Salida esperada:

ls: cannot access '/no-existe': No such file or directory
raw:
accesos.log
texto.txt

El orden de 2>&1 importa: primero se dice adónde va stdout (> out/listado.txt) y luego que stderr vaya “al mismo sitio que stdout”.

< hace que el programa lea un archivo como si lo escribieras tú. Compara:

wc -l raw/accesos.log
wc -l < raw/accesos.log

Salida esperada:

24 raw/accesos.log
24

En el segundo caso wc no sabe el nombre del archivo; solo recibe líneas. Esa forma limpia es la que conviene dentro de un script cuando solo quieres el número.

Error común: escribir > cuando querías >>. Un > sobre el log de ayer lo borra sin preguntar. Cuando un archivo acumula historia (una bitácora), usa siempre >>.


6. Tuberías: la idea central

Una tubería (pipe, el símbolo |) conecta la salida estándar de un comando con la entrada estándar del siguiente. Ningún resultado intermedio se guarda en disco: los datos fluyen de un programa a otro.

grep ' 500 ' raw/accesos.log | wc -l

Salida esperada:

2

grep deja pasar solo las líneas con código 500; wc -l las cuenta. Ninguno de los dos sabe que el otro existe: solo leen y escriben texto. Por eso se pueden combinar sin límite.

¿Por qué importa en Big Data? Porque es exactamente el modelo que verás en los capítulos de MapReduce y Spark: datos que pasan por una cadena de transformaciones pequeñas, cada una con una responsabilidad. La diferencia es que allá la cadena se reparte entre muchas máquinas.


7. Filtros: las herramientas que más vas a usar

grep: quedarse con las líneas que importan

Opción Qué hace
grep 'texto' archivo Líneas que contienen el texto
-i Ignora mayúsculas y minúsculas
-v Invierte: líneas que no contienen el texto
-c Cuenta las líneas en vez de mostrarlas
-n Muestra el número de línea
-E Expresiones regulares extendidas, como (500|503)
grep -c 'POST' raw/accesos.log
grep -n 'inventario' raw/accesos.log | head -n 3
grep -v ' 200 ' raw/accesos.log | wc -l

Salida esperada:

5
4:2026-10-01 08:02:45 10.0.0.20 GET /api/inventario 500 128 1203
9:2026-10-01 08:10:33 10.0.0.12 GET /api/inventario 200 3072 95
10:2026-10-01 08:12:09 10.0.0.20 GET /api/inventario 503 128 3001
8

Hay 5 peticiones POST, la primera mención de inventario está en la línea 4 y 8 peticiones no terminaron en 200.

Error común: buscar grep 200 sin espacios. También coincide con 2026-10-01 (la fecha empieza por “20”) y con tamaños como 2048. Busca ' 200 ', con los espacios que delimitan el campo, o usa awk (más abajo), que entiende columnas.

cut: quedarse con algunas columnas

cut -d' ' -f5 corta cada línea por el delimitador espacio (-d' ') y se queda con el campo 5 (-f5). Puedes pedir varios: -f5,6.

grep -v ' 200 ' raw/accesos.log | cut -d' ' -f5,6

Salida esperada:

/api/ventas 201
/api/inventario 500
/login 401
/api/inventario 503
/api/clientes 404
/api/inventario 500
/api/ventas 201
/api/clientes 403

sort y uniq: agrupar y contar

sort ordena líneas. uniq colapsa líneas consecutivas iguales; con -c antepone cuántas había. La palabra clave es consecutivas: uniq solo funciona bien sobre datos ya ordenados.

Opción de sort Qué hace
-n Orden numérico (10 va después de 9, no antes)
-r Orden inverso
-u Elimina duplicados al ordenar
-k1,1nr -k2,2 Ordena por la columna 1 numérica descendente y desempata por la columna 2

¿Cuántas peticiones hubo por código de estado?

cut -d' ' -f6 raw/accesos.log | sort | uniq -c

Salida esperada:

     16 200
      2 201
      1 401
      1 403
      1 404
      2 500
      1 503

¿Cuántos clientes distintos hubo?

cut -d' ' -f3 raw/accesos.log | sort -u | wc -l

Salida esperada:

5

¿Cuáles son las rutas más pedidas?

cut -d' ' -f5 raw/accesos.log | sort | uniq -c | sort -nr

Salida esperada:

      8 /api/ventas
      6 /api/inventario
      5 /login
      5 /api/clientes

Fíjate en el empate a 5: sort -nr invierte también el orden alfabético de los empatados, por eso /login aparece antes que /api/clientes. Si necesitas un desempate predecible, ordena por columnas: sort -k1,1nr -k2,2. Lo usarás en el mini-proyecto.

tr: transformar caracteres

tr reemplaza caracteres uno a uno. tr ' ' ',' cambia espacios por comas; tr ' ' '\n' cambia cada espacio por un salto de línea, es decir, pone una palabra por línea.

head -n 2 raw/accesos.log | tr ' ' ','

Salida esperada:

2026-10-01,08:00:01,10.0.0.12,GET,/api/ventas,200,5120,34
2026-10-01,08:00:03,10.0.0.15,GET,/api/clientes,200,2048,21

Acabas de convertir un log en un CSV. Ese tipo de conversión es el pan de cada día de una etapa de limpieza.

awk: un lenguaje pequeño para columnas

awk lee cada línea, la parte en campos ($1, $2, …, $8) y ejecuta un programa por línea. Su forma básica es condición {acción}. Un bloque END {...} se ejecuta una vez al final, cuando ya leyó todo.

Errores de servidor (código 500 o mayor), con la fecha, la hora, la ruta y el tiempo de respuesta:

awk '$6 >= 500 {print $1, $2, $5, $6, $8}' raw/accesos.log

Salida esperada:

2026-10-01 08:02:45 /api/inventario 500 1203
2026-10-01 08:12:09 /api/inventario 503 3001
2026-10-02 09:03:47 /api/inventario 500 1450

A diferencia de grep, awk compara el campo 6 como número: no se confunde con fechas ni tamaños.

Tiempo de respuesta promedio de todo el log:

awk '{suma += $8; n++} END {printf "%.1f ms promedio en %d peticiones\n", suma/n, n}' raw/accesos.log

Salida esperada:

270.4 ms promedio en 24 peticiones

Bytes enviados por día, agrupando con un arreglo asociativo (un diccionario cuya clave es la fecha):

awk '{bytes[$1] += $7} END {for (d in bytes) print d, bytes[d]}' raw/accesos.log | sort

Salida esperada:

2026-10-01 21632
2026-10-02 27584

awk no garantiza el orden en que recorre el arreglo; por eso terminamos con sort. Observa la forma del cálculo: por cada línea se emite una clave (la fecha) y un valor (los bytes), y al final se suman los valores de cada clave. Retén esa idea: es map y reduce.

tee: guardar sin cortar la tubería

tee copia lo que recibe a un archivo y lo deja seguir por la tubería. Sirve para guardar un resultado intermedio mientras lo sigues procesando:

cut -d' ' -f6 raw/accesos.log | sort | uniq -c | sort -nr | tee out/codigos.txt | head -n 3
wc -l out/codigos.txt

Salida esperada:

     16 200
      2 500
      2 201
7 out/codigos.txt

En pantalla viste solo los tres primeros, pero el archivo guardó los siete códigos.


8. El wordcount en una línea: tu primer MapReduce

Ahora junta lo aprendido sobre el texto de cuatro líneas. Ve construyendo la tubería paso a paso y ejecuta cada versión.

Paso 1 — una palabra por línea.

tr ' ' '\n' < raw/texto.txt

Salida esperada:

hadoop
hdfs
yarn
hadoop
mapreduce
yarn
spark
hadoop
datos
yarn
datos
datos

Paso 2 — ordenar, para que las palabras iguales queden juntas.

tr ' ' '\n' < raw/texto.txt | sort

Salida esperada:

datos
datos
datos
hadoop
hadoop
hadoop
hdfs
mapreduce
spark
yarn
yarn
yarn

Paso 3 — contar cada grupo.

tr ' ' '\n' < raw/texto.txt | sort | uniq -c

Salida esperada:

      3 datos
      3 hadoop
      1 hdfs
      1 mapreduce
      1 spark
      3 yarn

Seis palabras distintas que suman 12 apariciones, el mismo 12 que dio wc. Este es el famoso wordcount.

¿Y si te saltas el sort? Pruébalo:

tr ' ' '\n' < raw/texto.txt | uniq -c

Salida esperada:

      1 hadoop
      1 hdfs
      1 yarn
      1 hadoop
      1 mapreduce
      1 yarn
      1 spark
      1 hadoop
      1 datos
      1 yarn
      2 datos

Sin ordenar, uniq solo junta repeticiones vecinas y el conteo sale mal. Agrupar por clave antes de agregar no es un detalle: es la condición para que el resultado sea correcto.

Lo que acabas de construir

Paso en tu terminal Fase de MapReduce Qué hace
tr ' ' '\n' Map Parte cada línea y emite una palabra (una clave) por aparición
sort Shuffle Reúne las claves iguales
uniq -c Reduce Agrega cada grupo en un resultado

Cuando llegues al capítulo de MapReduce verás esta misma tabla con otros nombres: el mapper emite palabra 1, Hadoop ordena y agrupa por clave entre máquinas, y el reducer suma. En el taller de Hadoop Streaming escribirás el mapper y el reducer en Python y los probarás primero en una sola máquina, ordenando entre uno y otro igual que aquí, antes de enviarlos al clúster.

La diferencia de escala es la única diferencia de fondo: aquí sort ordena en una sola máquina; en Hadoop el shuffle mueve claves por la red entre decenas de nodos. La idea es la misma.


9. Buscar y operar sobre muchos archivos

En un data lake rara vez trabajas con un archivo: trabajas con cientos, normalmente particionados por fecha. Separa el log en un archivo por día dentro de clean/:

awk '{print > ("clean/accesos_" $1 ".log")}' raw/accesos.log
ls clean
wc -l clean/*.log

Salida esperada:

accesos_2026-10-01.log
accesos_2026-10-02.log
  12 clean/accesos_2026-10-01.log
  12 clean/accesos_2026-10-02.log
  24 total

Cada línea fue a un archivo cuyo nombre depende de su fecha ($1). Es la misma idea de particionar que verás en HDFS y en Spark al escribir datos por día.

find: localizar archivos por criterios

find . -name '*.log' -type f | sort

Salida esperada:

./clean/accesos_2026-10-01.log
./clean/accesos_2026-10-02.log
./raw/accesos.log

find recorre las carpetas desde el punto indicado (., la actual) y aplica filtros: por nombre (-name), por tipo (-type f para archivos, -type d para carpetas), por antigüedad (-mtime +7, más de 7 días) o por tamaño (-size +100M). Pon el patrón entre comillas para que lo interprete find y no Bash.

xargs: convertir una lista en argumentos

Muchos comandos esperan nombres de archivo como argumentos, no por stdin. xargs hace el puente:

find clean -name '*.log' -print0 | xargs -0 wc -l

Salida esperada:

  12 clean/accesos_2026-10-01.log
  12 clean/accesos_2026-10-02.log
  24 total

-print0 y -0 separan los nombres con un carácter nulo en lugar de espacios, para que un nombre con espacios no se parta en dos. Es la forma segura y conviene usarla siempre.

tar: empaquetar y comprimir

tar -czf out/respaldo_clean.tar.gz clean
tar -tzf out/respaldo_clean.tar.gz

Salida esperada (el orden de las líneas puede variar):

clean/
clean/accesos_2026-10-01.log
clean/accesos_2026-10-02.log
Opción Significado
-c Crear un paquete
-t Listar su contenido sin extraer
-x Extraer
-z Comprimir o descomprimir con gzip
-f El nombre del archivo, siempre al final de las opciones

Así se distribuyen Hadoop y Spark: hadoop-3.4.1.tar.gz se descarga y se extrae con tar -xzf.

Error común: usar rm con comodines sin mirar antes qué coincide. Antes de cualquier rm *.log, ejecuta ls *.log y lee la lista. En un nodo de producción no hay papelera de reciclaje.


10. Procesos: qué está corriendo

Un servicio de Big Data es un proceso: un programa en ejecución con un número de identificación (PID). Tres comandos te dan el panorama:

Comando Qué muestra
ps aux Todos los procesos, con usuario, PID, CPU, memoria y comando
ps aux + grep java Solo los procesos de Java (NameNode, DataNode, Spark… son JVM): ps aux y su salida filtrada con grep java
top o htop Vista en vivo de CPU y memoria (q para salir)
kill PID Pide a un proceso que termine ordenadamente

En un nodo de Hadoop, ps aux | grep java es la forma más rápida de saber si el DataNode sigue vivo. En los laboratorios con Docker harás lo mismo con docker compose ps y docker compose logs, que aplican esta idea a contenedores.

Buenas prácticas: kill -9 fuerza la terminación sin dejar al proceso guardar su estado. Úsalo solo si kill normal no funcionó, y nunca sobre un NameNode con datos.


11. Mini-proyecto: un reporte de accesos reproducible

Todo lo anterior lo hiciste a mano, comando por comando. El último paso de un ingeniero es convertir esas preguntas en un script: un archivo que cualquiera puede ejecutar mañana y obtener el mismo reporte.

El reporte responderá cinco preguntas sobre raw/accesos.log:

  1. ¿Cuántas peticiones y cuántos clientes distintos hubo?
  2. ¿Cuántas peticiones terminaron con cada código de estado?
  3. ¿Qué rutas se pidieron más?
  4. ¿Qué errores de servidor hubo y cuánto tardaron?
  5. ¿Cuántos bytes se enviaron por día y cuál fue la latencia promedio de cada ruta?

Crea el script:

cd ~/bigdata_linux_lab
cat > scripts/reporte_accesos.sh <<'EOF'
#!/usr/bin/env bash
# Reporte de accesos: lee raw/accesos.log y escribe out/reporte_accesos.txt
# Uso: bash scripts/reporte_accesos.sh   (desde ~/bigdata_linux_lab)
set -euo pipefail

ENTRADA="raw/accesos.log"
SALIDA="out/reporte_accesos.txt"
BITACORA="logs/reporte_accesos.log"

if [ ! -f "$ENTRADA" ]; then
  echo "ERROR: no existe $ENTRADA" >&2
  exit 1
fi

{
  echo "== Reporte de accesos =="
  echo "Archivo: $ENTRADA"
  echo "Peticiones: $(wc -l < "$ENTRADA")"
  echo "Clientes distintos: $(cut -d' ' -f3 "$ENTRADA" | sort -u | wc -l)"
  echo
  echo "-- Peticiones por código de estado --"
  cut -d' ' -f6 "$ENTRADA" | sort | uniq -c | sort -k1,1nr -k2,2
  echo
  echo "-- Rutas más pedidas --"
  cut -d' ' -f5 "$ENTRADA" | sort | uniq -c | sort -k1,1nr -k2,2
  echo
  echo "-- Errores de servidor (5xx) --"
  awk '$6 >= 500 {print $1, $2, $5, $6, $8 " ms"}' "$ENTRADA"
  echo
  echo "-- Bytes enviados por día --"
  awk '{b[$1] += $7} END {for (d in b) print d, b[d]}' "$ENTRADA" | sort
  echo
  echo "-- Latencia promedio por ruta (ms) --"
  awk '{s[$5] += $8; n[$5]++} END {for (r in s) printf "%s %.1f\n", r, s[r]/n[r]}' "$ENTRADA" | sort
} > "$SALIDA"

echo "$(date -Iseconds) reporte generado en $SALIDA" >> "$BITACORA"
echo "Listo: $SALIDA"
EOF

Qué hace cada parte:

Línea Para qué
#!/usr/bin/env bash El shebang: indica con qué intérprete se ejecuta el archivo
set -euo pipefail Detiene el script ante el primer error (-e), ante una variable no definida (-u) o si falla cualquier comando de una tubería (pipefail)
Variables en mayúsculas Las rutas se escriben una sola vez; cambiar el archivo de entrada es cambiar una línea
if [ ! -f ... ] Falla con un mensaje claro si falta la entrada, y lo envía a stderr con >&2
{ ... } > "$SALIDA" Todo lo que imprime el bloque va a un único archivo
>> "$BITACORA" Cada ejecución deja una línea en la bitácora, sin borrar las anteriores

Revisa la sintaxis sin ejecutar, dale permiso de ejecución y córrelo:

bash -n scripts/reporte_accesos.sh
chmod +x scripts/reporte_accesos.sh
./scripts/reporte_accesos.sh
cat out/reporte_accesos.txt

Salida esperada:

Listo: out/reporte_accesos.txt
== Reporte de accesos ==
Archivo: raw/accesos.log
Peticiones: 24
Clientes distintos: 5

-- Peticiones por código de estado --
     16 200
      2 201
      2 500
      1 401
      1 403
      1 404
      1 503

-- Rutas más pedidas --
      8 /api/ventas
      6 /api/inventario
      5 /api/clientes
      5 /login

-- Errores de servidor (5xx) --
2026-10-01 08:02:45 /api/inventario 500 1203 ms
2026-10-01 08:12:09 /api/inventario 503 3001 ms
2026-10-02 09:03:47 /api/inventario 500 1450 ms

-- Bytes enviados por día --
2026-10-01 21632
2026-10-02 27584

-- Latencia promedio por ruta (ms) --
/api/clientes 17.2
/api/inventario 989.7
/api/ventas 49.2
/login 14.4

bash -n no imprime nada si la sintaxis es correcta. Ejecuta el script una segunda vez y mira la bitácora:

./scripts/reporte_accesos.sh
cat logs/reporte_accesos.log

Verás dos líneas con fecha y hora: una por ejecución.

Qué dice el reporte

Un reporte no termina en los números; termina en una conclusión. Este dice algo concreto: los tres errores de servidor ocurrieron en /api/inventario, y esa ruta tiene una latencia promedio de casi un segundo frente a menos de 50 ms del resto. Antes de mirar Spark o la base de datos, el primer sospechoso es el servicio de inventario. Llegaste ahí con cinco herramientas de texto y sin una línea de Python.

Retos

  1. Agrega al reporte una sección con las peticiones por cliente (campo 3), ordenadas de mayor a menor.
  2. Haz que el script reciba el archivo de entrada como argumento: ENTRADA="${1:-raw/accesos.log}". Pruébalo con ./scripts/reporte_accesos.sh clean/accesos_2026-10-02.log. ¿Cuántas peticiones tiene ese día?
  3. ¿Qué pasa si ejecutas el script desde otra carpeta, por ejemplo desde ~? Explica el error y propón una corrección (pista: cd "$(dirname "$0")/.." al inicio).

12. Errores comunes

Síntoma Causa probable Solución
command not found El programa no está instalado o no está en el PATH which programa; instálalo con sudo apt install
No such file or directory Estás en otra carpeta o el nombre está mal escrito pwd y ls; usa Tab para completar nombres
Permission denied al ejecutar ./script.sh Falta el permiso de ejecución chmod +x script.sh, o ejecútalo con bash script.sh
El conteo de uniq -c sale repetido o incompleto La entrada no estaba ordenada Pon sort antes de uniq
grep 200 cuenta de más Coincide también con fechas y tamaños Busca ' 200 ' o filtra por columna con awk '$6 == 200'
La terminal se queda esperando Falta cerrar comillas o el EOF de un here document Ctrl+C y vuelve a copiar el bloque completo
Se perdió el contenido de un archivo Usaste > en vez de >> Para bitácoras, usa siempre >>
El script sigue a pesar de un error Falta set -euo pipefail Ponlo como primera línea después del shebang

13. Preguntas de cierre

  1. ¿Qué diferencia hay entre > archivo y >> archivo, y cuál usarías para una bitácora?
  2. ¿Por qué uniq -c necesita una entrada ordenada? Relaciónalo con la fase de shuffle.
  3. En cut -d' ' -f6 raw/accesos.log | sort | uniq -c, ¿qué fase de MapReduce representa cada comando?
  4. ¿Por qué awk '$6 >= 500' es más confiable que grep ' 5' para encontrar errores de servidor?
  5. ¿Qué ventaja tiene guardar el análisis en un script frente a repetir los comandos a mano?

Respuestas orientativas: > reemplaza y >> agrega, así que una bitácora siempre lleva >>. uniq solo agrupa líneas vecinas, igual que un reducer solo puede sumar cuando el shuffle ya reunió todas las apariciones de una clave. cut extrae la clave (map), sort agrupa (shuffle) y uniq -c agrega (reduce). awk compara el campo como número; grep ' 5' coincide con cualquier campo que empiece por 5 tras un espacio. El script es reproducible, se puede revisar y se puede programar para que corra solo.


Referencias para profundizar


Resumen

En este capítulo aprendiste:

  • La anatomía de un comando (programa, opciones y argumentos) y cómo pedir ayuda con --help, man y type.
  • Cómo Bash interpreta comillas, variables, comodines y llaves antes de ejecutar un comando.
  • Los tres flujos estándar y las redirecciones >, >>, 2>, 2>&1 y <.
  • A encadenar herramientas con tuberías y a usar los filtros esenciales: grep, cut, sort, uniq, tr, awk y tee.
  • Que tr | sort | uniq -c es un wordcount completo y que sus tres pasos corresponden a map, shuffle y reduce.
  • A operar sobre muchos archivos con find, xargs y tar, y a ver qué procesos corren en una máquina.
  • A convertir un análisis en un script reproducible con set -euo pipefail y una bitácora.

Con esto ya no solo te orientas en Linux: lo usas para responder preguntas sobre datos. En el siguiente capítulo, Docker para Big Data, empaquetarás entornos completos en contenedores, la herramienta con la que montaremos el clúster del resto del libro.