Cuando JavaScript sale del navegador
\n\nLa mayoría de los desarrolladores están acostumbrados a ver JavaScript como un lenguaje para «dar vida» a las páginas web. Pero, ¿y si te digo que con JavaScript se pueden escribir scripts para gestionar el sistema operativo, trabajar con el sistema de archivos a bajo nivel e incluso crear demonios de red? ¿Suena a ciencia ficción? Pues no. Gracias a entornos de ejecución como Node.js y Deno, el JavaScript para programación de sistemas se ha convertido no solo en algo posible, sino en una herramienta bastante eficaz. En este artículo analizaremos ejemplos de código reales y veremos cómo JS maneja tareas que antes se consideraban dominio exclusivo de C, Rust o Python.
\n\n¿Por qué JavaScript? Ventajas y desventajas
\n\nAntes de sumergirnos en el código, evaluemos honestamente la situación. La programación de sistemas con Node.js tiene sus fortalezas y debilidades.
\n\nVentajas:
\n- \n
- Pila tecnológica unificada — si eres desarrollador frontend, no necesitas aprender un nuevo lenguaje para escribir utilidades de línea de comandos. \n
- Comunidad enorme — npm ofrece miles de paquetes para trabajar con el sistema. \n
- Asincronía incorporada — la E/S sin bloqueo es ideal para trabajar con red y archivos. \n
- Prototipado rápido — se puede escribir un script en JS en cuestión de minutos. \n
Desventajas:
\n- \n
- Rendimiento — JS es más lento que C o Rust en tareas computacionales. \n
- Acceso limitado al hardware — no hay trabajo directo con memoria o puertos de E/S (aunque existen soluciones alternativas). \n
- Consumo de memoria — V8 no es el motor más ligero. \n
Sin embargo, para el 80% de las tareas de administración de sistemas y automatización, el JavaScript para programación de sistemas es perfectamente adecuado. Pasemos a la práctica.
\n\nTrabajo con el sistema de archivos: módulo fs
\n\nEmpecemos por lo básico: el trabajo con archivos en JS. El módulo fs en Node.js proporciona métodos tanto síncronos como asíncronos para interactuar con el sistema de archivos.
Lectura y escritura de archivos (versión asíncrona)
\n\nconst fs = require('fs').promises;\nconst path = require('path');\n\nasync function processLogFile(filePath) {\n try {\n // Leemos el archivo\n const data = await fs.readFile(filePath, 'utf-8');\n const lines = data.split('\\n');\n \n // Filtramos solo las líneas con errores\n const errors = lines.filter(line => line.includes('ERROR'));\n \n // Escribimos el resultado\n const outputPath = path.join(__dirname, 'errors.log');\n await fs.writeFile(outputPath, errors.join('\\n'), 'utf-8');\n \n console.log(`Procesadas ${lines.length} líneas, encontrados ${errors.length} errores`);\n } catch (err) {\n console.error('Error al trabajar con el archivo:', err);\n }\n}\n\nprocessLogFile('/var/log/application.log');\n\nEste script es un ejemplo típico de un script de sistema en JavaScript. Lee un archivo de registro grande, filtra los datos y escribe el resultado. La versión asíncrona no bloquea el bucle de eventos, lo cual es crítico para aplicaciones de servidor.
\n\nMonitoreo de cambios en el sistema de archivos
\n\nUna de las capacidades potentes es la vigilancia de archivos en tiempo real:
\n\nconst fs = require('fs');\n\nconst watcher = fs.watch('/etc/config', { recursive: true });\n\nwatcher.on('change', (eventType, filename) => {\n console.log(`Cambio en el archivo: ${filename} (${eventType})`);\n \n // Recargamos la configuración automáticamente\n if (filename.endsWith('.conf')) {\n const config = fs.readFileSync(`/etc/config/${filename}`, 'utf-8');\n console.log('Nueva configuración cargada');\n }\n});\n\nconsole.log('Monitoreo de configuraciones iniciado');