Skip to content

~/bda/m00 $ cat 05-proyecto-captura-local.md

  • Big Data Aplicado
  • M00
  • lección 05/05

Proyecto de captura local de cotizaciones

Entregable acumulativo de M00.

Aules

Este proyecto ocupa la última sesión de M00 e integra lo trabajado en los bloques anteriores. El resultado será un script Bash que reciba un par de cotización y un precio de prueba, valide su formato y añada una fila a un CSV con la fecha y hora UTC de la captura. Dejará constancia de los resultados en un log y se ejecutará periódicamente con cron, protegido por flock.

Los precios son simulados y los eliges tú. Entregarás el código y las comprobaciones en un repositorio privado de GitHub con acceso al docente.

  • Integrar argumentos, validación, CSV, logs y códigos de salida en Bash.
  • Comprobar el script y automatizarlo sin solapamientos.
  • Documentar las pruebas y conservar una versión en GitHub.

Antes de empezar: cómo leer una cotización

Section titled “Antes de empezar: cómo leer una cotización”

En este proyecto, symbol identifica un par completo. En BTCUSDT, BTC es el activo base y USDT es la unidad del precio. Un valor ficticio de 65000.50 significa que 1 BTC vale 65 000,50 USDT.

El script recibirá estos dos argumentos:

Terminal window
./scripts/capturar_cotizacion.sh BTCUSDT 65000.50

Usaremos símbolos en mayúsculas y sin separadores, y precios con punto decimal y sin separador de miles.

USD y USDT son distintos: USD es el dólar estadounidense; USDT es un criptoactivo diseñado para mantener un valor próximo a un dólar, sin garantizar una equivalencia exacta.

1. Recuperar el trabajo y fijar el resultado

Section titled “1. Recuperar el trabajo y fijar el resultado”

Reutiliza bda-m00-debian y el bind mount de la página 01. Si necesitas abrir una terminal, utiliza en el anfitrión la orden correspondiente:

Estado del contenedor Orden
Detenido docker start -ai bda-m00-debian
En marcha docker exec -it bda-m00-debian bash

Desde aquí hasta el apartado de GitHub, trabaja dentro de Debian, como root, en /bda-m00:

Terminal window
whoami
cd /bda-m00
pwd

Organiza el trabajo con esta estructura. Los archivos de datos y logs aparecerán al ejecutar el script y la tarea programada:

/bda-m00/
├── scripts/
│ └── capturar_cotizacion.sh
├── data/
│ └── raw/
│ └── cotizaciones.csv
├── logs/
│ ├── capturas.log
│ └── cron.log
└── entregas/
└── comprobaciones-captura.md
Archivo Función
scripts/capturar_cotizacion.sh Código del capturador.
data/raw/cotizaciones.csv Capturas aceptadas, una por fila.
logs/capturas.log Resultados y rechazos registrados por el script.
logs/cron.log Salida y errores de la orden programada.

El símbolo y el precio proceden de los argumentos; la fecha procede de date -u. El CSV tendrá este formato, con la fecha real de tus pruebas:

timestamp,symbol,price
2026-10-22T10:15:00Z,BTCUSDT,65000.50

La T separa fecha y hora; la Z indica UTC, con desplazamiento cero (+00:00). Como utilizamos date -u, no hay que añadir GMT+2 ni sumar horas a mano. Estas dos marcas representan el mismo instante:

Marca temporal Interpretación
2026-10-22T10:15:00Z Las 10:15 en UTC.
2026-10-22T12:15:00+02:00 Las 12:15 con un desplazamiento de dos horas respecto a UTC.

Guardaremos siempre UTC con Z, independientemente de la zona horaria del equipo. Referencia: RFC 3339, fechas y horas.

Hora del sistema: activa la sincronización automática del reloj del anfitrión, que también utiliza el contenedor. date -u muestra la hora en UTC, pero no corrige un reloj incorrecto.

Los logs registran lo ocurrido. Ejemplos con fechas ilustrativas:

logs/capturas.log

[2026-10-22T10:15:00Z] OK captura guardada para BTCUSDT
[2026-10-22T10:15:20Z] ERROR símbolo inválido
[2026-10-22T10:16:00Z] OK captura guardada para BTCUSDT

Cada entrada lleva fecha y hora UTC, tanto en éxitos como en errores. Su fecha puede diferir ligeramente de la del CSV: se escriben en momentos distintos.

logs/cron.log

[2026-10-22T10:16:00Z] Captura guardada: BTCUSDT 65000.50
[2026-10-22T10:17:00Z] Captura guardada: BTCUSDT 65000.50

Recoge la salida y los errores de la tarea programada.

El script debe responder así:

Entrada Resultado Código
Dos argumentos con formato válido Añadir una fila, registrar OK captura guardada para <símbolo> y mostrar [<fecha UTC>] Captura guardada: <símbolo> <precio>. 0
Cantidad de argumentos distinta de dos Ayuda por stderr y log ERROR uso incorrecto: se esperaban 2 argumentos; CSV intacto. 64
Símbolo incorrecto Diagnóstico por stderr y log ERROR símbolo inválido; CSV intacto. 64
Precio incorrecto Diagnóstico por stderr y log ERROR precio inválido para <símbolo>; CSV intacto. 64

Usa exit 64 para los errores de entrada; es una convención del proyecto. Consulta el código con echo "$?" inmediatamente después de ejecutar el script.

Flujo del proyecto: argumentos, validación Bash, CSV, logs, cron con flock y transición a Airflow en M02.

Airflow aparece como continuidad hacia M02.

Crea el archivo con Nano o Vim:

Terminal window
nano scripts/capturar_cotizacion.sh

Escribe dentro del archivo los seis pasos siguientes. El bloque 03 de Bash sirve como referencia.

Selecciona Bash con el shebang y aplica set -euo pipefail. Define base_dir como /bda-m00 y construye a partir de él estas variables:

Variable Ruta
data_dir /bda-m00/data/raw
log_dir /bda-m00/logs
output_file /bda-m00/data/raw/cotizaciones.csv
log_file /bda-m00/logs/capturas.log

Las rutas absolutas permitirán ejecutar el script desde otra carpeta.

Crear las carpetas desde el script. Después de definir las variables, añade una instrucción que cree data/raw y logs si todavía no existen. Utiliza mkdir -p con "$data_dir" y "$log_dir". Debe ejecutarse antes de guardar datos o llamar a la función log; si las carpetas ya existen, conservará su contenido.

Crea una función llamada log que reciba un mensaje, le añada la fecha UTC y lo guarde al final del archivo indicado por log_file. Necesitarás date -u '+%Y-%m-%dT%H:%M:%SZ', $(...), "$*" y printf con >>.

Una vez definida, prueba esta llamada dentro del script:

Terminal window
log "PRUEBA registro preparado"

El resultado en capturas.log será una línea como esta:

[2026-10-22T10:15:00Z] PRUEBA registro preparado

log es la función; log_file, la ruta del archivo. La carpeta de logs debe existir antes de la primera llamada. Comprueba la línea con tail -n 1 logs/capturas.log en la terminal y retira la llamada de prueba. Conserva la función para registrar éxitos y errores.

Comprueba con $# que hay exactamente dos argumentos. Si no, registra el error de 1.2, muestra la ayuda por stderr con fecha UTC y termina con exit 64.

Después de ese if, guarda los argumentos:

Terminal window
symbol="$1"
price="$2"

En una llamada con BTCUSDT 65000.50, symbol contendrá BTCUSDT y price, 65000.50. La comprobación debe ir antes: con set -u, leer un argumento inexistente detendría el script antes de mostrar tu ayuda.

Campo Formato admitido Ejemplos válidos Ejemplos inválidos
Símbolo Entre 3 y 20 caracteres: mayúsculas o dígitos (límite del ejercicio). BTCUSDT, ETHUSDT btc/usdt, BTC-USDT, AB
Precio Uno o más dígitos, con parte decimal opcional: punto seguido de uno o más dígitos. 65000.50, 3200, 0 65000,50, texto, -5, 3.

Ante un rechazo, registra el motivo, muestra el diagnóstico por stderr con fecha UTC y termina con 64. Usa los mensajes de 1.2 sin copiar el argumento inválido al log: podría contener saltos de línea.

Se valida el formato; no se comprueba que el par exista ni que el precio sea real. 0 es válido.

Si el archivo csv no existe, debes escribir en primer lugar su cabecera: timestamp,symbol,price.

Obtén la fecha UTC en timestamp con el formato del paso 2. Añade los tres valores a output_file, separados por comas y con salto de línea, mediante printf y >>. Después llama a log con el mensaje de éxito de 1.2 y muestra el resumen por pantalla con este patrón: [<fecha UTC>] Captura guardada: <símbolo> <precio>. Reutiliza timestamp para la fecha del resumen.

Cada ejecución guarda una captura, sin bucle. Si el log falla después de escribir el CSV, set -e no deshace esa fila.

Desde /bda-m00, revisa la versión completa:

Terminal window
chmod u+x scripts/capturar_cotizacion.sh
shellcheck scripts/capturar_cotizacion.sh

Corrige los avisos de ShellCheck antes de probar el comportamiento del script.

Ejecuta cada caso por separado y consulta echo "$?" inmediatamente después. Para comparar el CSV utiliza wc -l; para leer sus extremos, head -n 1 y tail. Anota resultados en entregas/comprobaciones-captura.md.

Prueba Orden o actuación Resultado esperado
Captura válida ./scripts/capturar_cotizacion.sh BTCUSDT 65000.50 Código 0, una fila más y mensaje OK en logs/capturas.log.
Otro símbolo válido ./scripts/capturar_cotizacion.sh ETHUSDT 3200 Código 0 y otra fila; cabecera una sola vez.
Cantidad incorrecta Llama al script sin argumentos, con solo BTCUSDT y con BTCUSDT 65000.50 extra. Cada llamada devuelve 64, ayuda por stderr y error en el log; CSV intacto.
Símbolo incorrecto ./scripts/capturar_cotizacion.sh btc/usdt 65000.50 Código 64, diagnóstico del símbolo y error en el log; CSV intacto.
Precio incorrecto ./scripts/capturar_cotizacion.sh BTCUSDT texto Código 64, diagnóstico del precio y error en el log; CSV intacto.
Otra carpeta Desde /bda-m00/entregas, llama a /bda-m00/scripts/capturar_cotizacion.sh BTCUSDT 65000.50. Después vuelve a /bda-m00. Código 0; la fila se añade al mismo CSV de data/raw.

Tras las dos capturas válidas, el CSV tendrá tres líneas: cabecera y dos datos. Los rechazos no deben aumentar el recuento.

Dentro del contenedor, como root, comprueba service cron status. Si está detenido, usa service cron start.

Después de reiniciar el contenedor: vuelve a comprobar cron y arráncalo si hace falta. Este laboratorio inicia Bash como proceso principal, no un sistema de arranque que active los servicios. service cron start lo inicia para la sesión actual del contenedor; no configura su arranque automático. La tabla de tareas se conserva al detenerlo, pero necesita cron en marcha para ejecutarse.

Consulta crontab -l y conserva las tareas ajenas al proyecto.

¿Para qué sirve flock? Si una captura tarda más de un minuto, cron podría iniciar la siguiente antes de que termine. Ambas intentarían escribir en el mismo CSV. flock permite que solo una ejecución a la vez use un mismo bloqueo.

En la orden siguiente, /bda-m00/.captura.lock identifica ese bloqueo:

  • Si está libre, flock lo obtiene y ejecuta el script. Al terminar, lo libera.
  • Si otra ejecución lo mantiene, la opción -n significa no esperar: se omite esa captura. Cron volverá a intentarlo en el siguiente minuto.

Este mecanismo funciona entre las llamadas que utilizan el mismo bloqueo; no impide ejecutar directamente el script sin flock. Referencia: manual de flock.

Abre crontab -e y añade una sola tarea de captura cada minuto, con una única definición de PATH:

PATH=/usr/local/bin:/usr/bin:/bin
* * * * * /usr/bin/flock -n /bda-m00/.captura.lock /bda-m00/scripts/capturar_cotizacion.sh BTCUSDT 65000.50 >> /bda-m00/logs/cron.log 2>&1

La salida del script se guarda en cron.log; la función log escribe en capturas.log. Si flock omite una captura porque el bloqueo está ocupado, el script no llega a ejecutarse y no se añade ningún mensaje a esos logs.

Guarda, verifica crontab -l y anota las líneas actuales del CSV. Tras el siguiente cambio de minuto, con el contenedor en marcha, consulta:

Terminal window
tail -n 3 logs/cron.log
tail -n 3 logs/capturas.log
tail -n 3 data/raw/cotizaciones.csv
wc -l data/raw/cotizaciones.csv

Comprueba que hay una fila nueva y mensajes en ambos logs. El precio se repite porque los argumentos son fijos; cambia la fecha.

Al terminar, retira solo esta tarea con crontab -e, sin borrar los archivos. Como variante de consulta, 0 * * * * programaría la captura al inicio de cada hora.

Abre dos terminales del mismo contenedor. En la primera mantén el bloqueo durante quince segundos:

Terminal window
flock -n /bda-m00/.captura.lock sleep 15

Mientras tanto, ejecuta en la segunda:

Terminal window
flock -n /bda-m00/.captura.lock /bda-m00/scripts/capturar_cotizacion.sh ETHUSDT 3200
echo "$?"

Debe devolver 1 mientras el bloqueo está ocupado y 0 al repetir tras la espera. Si cron está activo, usa también este bloqueo en las capturas manuales. No borres .captura.lock: puede existir sin estar ocupado.

Conserva en el laboratorio el script, el CSV con cabecera y al menos dos filas válidas, y los dos logs. En entregas/comprobaciones-captura.md reúne:

  • El resultado de ShellCheck y una tabla breve con las pruebas de 3: orden, código observado y efecto sobre CSV/log.
  • La entrada de cron, una evidencia de su ejecución y el resultado de la prueba del bloqueo.
  • La explicación de un rechazo que hayas provocado: qué esperabas y cómo comprobaste que no añadió una fila.

Incluye resultados propios y extractos breves. Los CSV y logs completos se quedan en el laboratorio.

Síntoma Comprobación
No encuentra el script o los archivos. Revisa pwd, nombres y rutas absolutas.
Permission denied. Revisa ls -l y los límites del bind mount de 01; no cambies permisos de toda la carpeta.
Un dato esperado como válido devuelve 64. Revisa mayúsculas, punto decimal y ausencia de separadores de miles.
El CSV empieza por otra cosa. Conserva una copia y revisa la cabecera antes de continuar.
Funciona manualmente pero cron no captura. Revisa servicio, contenedor activo, tabla, rutas, permisos, cron.log y bloqueo, como en 04.

6. Subir el trabajo a un repositorio privado

Section titled “6. Subir el trabajo a un repositorio privado”

Trabaja en el anfitrión, con tu cuenta habitual, desde la carpeta local bda-m00 compartida con el contenedor. Versionarás los mismos archivos que has utilizado en la práctica.

Necesitas una cuenta GitHub, Git (git --version) y autenticación HTTPS preparada con Git Credential Manager o GitHub CLI. Consulta la guía de autenticación de GitHub si aún no la tienes configurada.

En GitHub, crea el repositorio crypto-data-pipeline, con visibilidad Private y vacío: sin README, licencia ni .gitignore. Los archivos se subirán desde tu equipo. Copia su URL HTTPS desde Code o desde las instrucciones iniciales del repositorio vacío.

En la terminal del anfitrión, situada en tu carpeta bda-m00, ejecuta:

git init -b main
git config user.name "Tu nombre"
git config user.email "Tu correo de autoría"

Sustituye los valores por tu identidad y un correo de tu cuenta GitHub; puedes utilizar el noreply de sus ajustes. init prepara el repositorio local y la configuración se aplica solo a él. Si bda-m00 ya pertenece a un repositorio o el remoto ya contiene archivos, consulta al docente antes de repetir esta preparación.

Crea README.md en bda-m00 con tu editor habitual. Explica qué hace el script, sus dos argumentos y cómo ejecutarlo dentro del contenedor, en /bda-m00. Aclara que los precios son ficticios y se introducen como argumentos. Indica que el CSV contiene las columnas timestamp,symbol,price y que los registros se guardan en logs/capturas.log y logs/cron.log.

En esa misma carpeta, crea .gitignore con estas reglas:

/data/
/logs/
/ssh/
/versionado-m00/
/entregas/crypto-data-pipeline/
.captura.lock
.env
*.pem
*.bak*

Estas reglas excluyen datos, logs, archivos SSH y copias de trabajo. No borran archivos ni retiran los que Git ya estuviera siguiendo. Revisa siempre qué vas a registrar.

Los cuatro archivos de esta entrega se encuentran en su ubicación de trabajo:

bda-m00/
├── .gitignore
├── README.md
├── scripts/
│ └── capturar_cotizacion.sh
└── entregas/
└── comprobaciones-captura.md

Las carpetas data y logs no se suben al repositorio. Al descargarlo en otro equipo, el script las creará en su primera ejecución, como se pide en el paso 1. El nombre local bda-m00 puede ser distinto del nombre del repositorio remoto crypto-data-pipeline.

Desde bda-m00, prepara únicamente los cuatro archivos de la entrega:

git status
git add .gitignore README.md scripts/capturar_cotizacion.sh entregas/comprobaciones-captura.md
git diff --staged
git commit -m "Entregar captura local de M00"
git remote add origin URL_HTTPS_COPIADA
git push -u origin main

Sustituye URL_HTTPS_COPIADA por la URL real de tu repositorio vacío. add selecciona los archivos y diff --staged permite revisarlos antes de registrar la versión con commit. No incluyas credenciales, claves ni otros archivos del laboratorio.

remote add conecta con GitHub y push envía los commits. -u asocia la rama local con la remota: en las siguientes entregas bastará git push, después de preparar y registrar los cambios. La conexión del remoto se configura una sola vez; puedes consultarla con git remote -v.

Comprueba en GitHub que aparecen los cuatro archivos, con sus rutas originales, y que la visibilidad sigue siendo Private. Si el envío falla, revisa el diagnóstico con el docente; no fuerces el push.

6.4. Invitar al docente y entregar el enlace

Section titled “6.4. Invitar al docente y entregar el enlace”

En tu repositorio de GitHub, abre Settings → Collaborators → Add people e invita a:

j.garciagarcia@edu.gva.es

Revisa la cuenta encontrada antes de confirmar. El docente tendrá acceso cuando acepte la invitación. Si no aparece por correo, pide su nombre de usuario.

Entrega en Aules el enlace al repositorio crypto-data-pipeline e indica si la invitación sigue pendiente. Mantén el repositorio privado y dedicado a la asignatura: el docente tendrá acceso a todo su contenido.

Referencia: invitaciones a repositorios personales en GitHub.

El resultado demuestra recepción y validación de argumentos, persistencia en CSV, registros y automatización local. El bloqueo evita solapamientos entre procesos que usan el mismo archivo de bloqueo; no proporciona coordinación entre equipos. Si se detiene el equipo o el contenedor, la captura se detiene: este entorno no demuestra alta disponibilidad, escalabilidad distribuida ni tolerancia a la caída del anfitrión.

M01 reutilizará el CSV para trabajar formatos y persistencia. En M02, una fuente externa sustituirá los valores simulados, Kafka desacoplará productor y consumidor y Airflow orquestará tareas con dependencias y recuperación. La hora de captura local no se equipara automáticamente al tiempo de evento de esa futura fuente.

Relación con resultados de aprendizaje y criterios de evaluación

Section titled “Relación con resultados de aprendizaje y criterios de evaluación”
RA / CE Evidencia
RA0.a), b), c) Script, logs, datos, crontab y explicación razonada de las decisiones tomadas. Git conserva y comparte la evidencia del código.