2 Dominando la terminal para Big Data
Comandos, filtros, tuberías y un mini-proyecto de logs sobre Linux
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:
- Haber completado el capítulo 1: Ubuntu 24.04 en WSL2 (o Linux nativo) y el directorio
~/bigdata_linux_labcon sus subcarpetasraw,clean,out,scripts,logsynotes - Una terminal Bash abierta
Tecnologías utilizadas:
- Bash
- Herramientas de GNU coreutils:
ls,cat,head,tail,wc,cut,sort,uniq,tr,tee grep,awk,find,xargsytar
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
grepen 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.txtCada 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 rawSalida 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 grepSalida 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
manno existe: algunas instalaciones mínimas de Ubuntu traen los manuales recortados.--helpfunciona 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 datoscuando la carpeta se llamamis datos. Bash entiende dos argumentos. La forma correcta escd "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-02Salida 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 produceraw/. - Flecha arriba recupera el comando anterior. Ctrl+R busca en el historial: escribe
grepy 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.txtSalida 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
catde un log de 20 GB: llenarás la pantalla durante minutos. Empieza siempre conhead,tailowc -lpara 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.txtSalida 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.txtSalida 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.logSalida 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 -lSalida 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 -lSalida 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 200sin espacios. También coincide con2026-10-01(la fecha empieza por “20”) y con tamaños como2048. Busca' 200 ', con los espacios que delimitan el campo, o usaawk(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,6Salida 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 -cSalida 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 -lSalida esperada:
5
¿Cuáles son las rutas más pedidas?
cut -d' ' -f5 raw/accesos.log | sort | uniq -c | sort -nrSalida 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.logSalida 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.logSalida 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 | sortSalida 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.txtSalida 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.txtSalida 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 | sortSalida 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 -cSalida 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 -cSalida 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/*.logSalida 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 | sortSalida 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 -lSalida 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.gzSalida 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
rmcon comodines sin mirar antes qué coincide. Antes de cualquierrm *.log, ejecutals *.logy 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 -9fuerza la terminación sin dejar al proceso guardar su estado. Úsalo solo sikillnormal 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:
- ¿Cuántas peticiones y cuántos clientes distintos hubo?
- ¿Cuántas peticiones terminaron con cada código de estado?
- ¿Qué rutas se pidieron más?
- ¿Qué errores de servidor hubo y cuánto tardaron?
- ¿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"
EOFQué 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.txtSalida 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.logVerá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
- Agrega al reporte una sección con las peticiones por cliente (campo 3), ordenadas de mayor a menor.
- 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? - ¿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
- ¿Qué diferencia hay entre
> archivoy>> archivo, y cuál usarías para una bitácora? - ¿Por qué
uniq -cnecesita una entrada ordenada? Relaciónalo con la fase de shuffle. - En
cut -d' ' -f6 raw/accesos.log | sort | uniq -c, ¿qué fase de MapReduce representa cada comando? - ¿Por qué
awk '$6 >= 500'es más confiable quegrep ' 5'para encontrar errores de servidor? - ¿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
- GNU, Bash Reference Manual: comillas, expansiones, redirecciones y tuberías.
- GNU, Coreutils:
sort,uniq,cut,tr,wc,head,tailytee. - GNU, Grep Manual.
- GNU, The GNU Awk User’s Guide. Ubuntu instala por defecto
mawk, compatible con todo lo que usa este capítulo. - Jeffrey Dean y Sanjay Ghemawat, MapReduce: Simplified Data Processing on Large Clusters (2004), para ver a dónde lleva la idea de la tubería.
Resumen
En este capítulo aprendiste:
- La anatomía de un comando (programa, opciones y argumentos) y cómo pedir ayuda con
--help,manytype. - Cómo Bash interpreta comillas, variables, comodines y llaves antes de ejecutar un comando.
- Los tres flujos estándar y las redirecciones
>,>>,2>,2>&1y<. - A encadenar herramientas con tuberías y a usar los filtros esenciales:
grep,cut,sort,uniq,tr,awkytee. - Que
tr | sort | uniq -ces un wordcount completo y que sus tres pasos corresponden a map, shuffle y reduce. - A operar sobre muchos archivos con
find,xargsytar, y a ver qué procesos corren en una máquina. - A convertir un análisis en un script reproducible con
set -euo pipefaily 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.