~/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
Introducción
Section titled “Introducción”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.
Objetivos
Section titled “Objetivos”- 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:
./scripts/capturar_cotizacion.sh BTCUSDT 65000.50Usaremos 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”1.1. Entrar en el entorno
Section titled “1.1. Entrar en el entorno”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:
whoamicd /bda-m00pwdOrganiza 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.md1.2. Archivos y comportamiento esperado
Section titled “1.2. Archivos y comportamiento esperado”| 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,price2026-10-22T10:15:00Z,BTCUSDT,65000.50La 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 -umuestra 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 BTCUSDTCada 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.50Recoge 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.
Airflow aparece como continuidad hacia M02.
2. Crear el capturador desde cero
Section titled “2. Crear el capturador desde cero”Crea el archivo con Nano o Vim:
nano scripts/capturar_cotizacion.shEscribe dentro del archivo los seis pasos siguientes. El bloque 03 de Bash sirve como referencia.
Paso 1. Arranque y rutas
Section titled “Paso 1. Arranque y rutas”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.
Paso 2. Función de registro
Section titled “Paso 2. Función de registro”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:
log "PRUEBA registro preparado"El resultado en capturas.log será una línea como esta:
[2026-10-22T10:15:00Z] PRUEBA registro preparadolog 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.
Paso 3. Cantidad de argumentos
Section titled “Paso 3. Cantidad de argumentos”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:
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.
Paso 4. Formato de los datos
Section titled “Paso 4. Formato de los datos”| 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.
Paso 5. Cabecera del CSV
Section titled “Paso 5. Cabecera del CSV”Si el archivo csv no existe, debes escribir en primer lugar su cabecera: timestamp,symbol,price.
Paso 6. Guardar la captura
Section titled “Paso 6. Guardar la captura”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.
3. Revisar y probar
Section titled “3. Revisar y probar”Desde /bda-m00, revisa la versión completa:
chmod u+x scripts/capturar_cotizacion.shshellcheck scripts/capturar_cotizacion.shCorrige 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.
4. Automatizar y comprobar el bloqueo
Section titled “4. Automatizar y comprobar el bloqueo”4.1. Una ejecución programada
Section titled “4.1. Una ejecución programada”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 startlo 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,
flocklo obtiene y ejecuta el script. Al terminar, lo libera. - Si otra ejecución lo mantiene, la opción
-nsignifica 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>&1La 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:
tail -n 3 logs/cron.logtail -n 3 logs/capturas.logtail -n 3 data/raw/cotizaciones.csvwc -l data/raw/cotizaciones.csvComprueba 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.
4.2. Una prueba de exclusión
Section titled “4.2. Una prueba de exclusión”Abre dos terminales del mismo contenedor. En la primera mantén el bloqueo durante quince segundos:
flock -n /bda-m00/.captura.lock sleep 15Mientras tanto, ejecuta en la segunda:
flock -n /bda-m00/.captura.lock /bda-m00/scripts/capturar_cotizacion.sh ETHUSDT 3200echo "$?"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.
5. Preparar las evidencias
Section titled “5. Preparar las evidencias”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.
Si algo no funciona
Section titled “Si algo no funciona”| 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”6.1. Preparar el repositorio
Section titled “6.1. Preparar el repositorio”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 maingit 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.
6.2. Elegir qué se versiona
Section titled “6.2. Elegir qué se versiona”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.mdLas 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.
6.3. Registrar y subir
Section titled “6.3. Registrar y subir”Desde bda-m00, prepara únicamente los cuatro archivos de la entrega:
git statusgit add .gitignore README.md scripts/capturar_cotizacion.sh entregas/comprobaciones-captura.mdgit diff --stagedgit commit -m "Entregar captura local de M00"git remote add origin URL_HTTPS_COPIADAgit push -u origin mainSustituye 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:
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.
7. Límites y continuidad
Section titled “7. Límites y continuidad”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. |