JavaScript в системном программировании: миф или реальность?

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Когда JavaScript выходит за пределы браузера

Большинство разработчиков привыкли воспринимать JavaScript как язык для «оживления» веб-страниц. Но что, если я скажу вам, что на JavaScript можно писать скрипты для управления операционной системой, работать с файловой системой на низком уровне и даже создавать сетевые демоны? Звучит как фантастика? А вот и нет. Благодаря средам выполнения вроде Node.js и Deno, JavaScript для системного программирования стал не просто возможным, а вполне эффективным инструментом. В этой статье мы разберем реальные примеры кода и посмотрим, как JS справляется с задачами, которые раньше считались прерогативой C, Rust или Python.

Почему JavaScript? Преимущества и подводные камни

Прежде чем нырять в код, давайте честно оценим ситуацию. Node.js системное программирование имеет свои сильные и слабые стороны.

Плюсы:

  • Единый стек технологий — если вы фронтенд-разработчик, вам не нужно учить новый язык для написания утилит командной строки.
  • Огромное сообщество — npm предлагает тысячи пакетов для работы с системой.
  • Асинхронность из коробки — неблокирующий ввод-вывод идеален для работы с сетью и файлами.
  • Быстрый прототипинг — написать скрипт на JS можно за считанные минуты.

Минусы:

  • Производительность — JS медленнее C или Rust в вычислительных задачах.
  • Ограниченный доступ к железу — прямой работы с памятью или портами ввода-вывода нет (хотя есть обходные пути).
  • Потребление памяти — V8 не самый легковесный движок.

Тем не менее, для 80% задач системного администрирования и автоматизации JavaScript для системного программирования подходит отлично. Давайте перейдем к практике.

Работа с файловой системой: модуль fs

Начнем с основ — работа с файлами JS. Модуль fs в Node.js предоставляет как синхронные, так и асинхронные методы для взаимодействия с файловой системой.

Чтение и запись файлов (асинхронный вариант)

const fs = require('fs').promises;
const path = require('path');

async function processLogFile(filePath) {
    try {
        // Читаем файл
        const data = await fs.readFile(filePath, 'utf-8');
        const lines = data.split('\n');
        
        // Фильтруем только строки с ошибками
        const errors = lines.filter(line => line.includes('ERROR'));
        
        // Записываем результат
        const outputPath = path.join(__dirname, 'errors.log');
        await fs.writeFile(outputPath, errors.join('\n'), 'utf-8');
        
        console.log(`Обработано ${lines.length} строк, найдено ${errors.length} ошибок`);
    } catch (err) {
        console.error('Ошибка при работе с файлом:', err);
    }
}

processLogFile('/var/log/application.log');

Этот скрипт — типичный пример системного скрипта на JavaScript. Он читает большой лог-файл, фильтрует данные и записывает результат. Асинхронная версия не блокирует event loop, что критично для серверных приложений.

Мониторинг изменений в файловой системе

Одна из мощных возможностей — слежение за файлами в реальном времени:

const fs = require('fs');

const watcher = fs.watch('/etc/config', { recursive: true });

watcher.on('change', (eventType, filename) => {
    console.log(`Изменение в файле: ${filename} (${eventType})`);
    
    // Автоматически перезагружаем конфигурацию
    if (filename.endsWith('.conf')) {
        const config = fs.readFileSync(`/etc/config/${filename}`, 'utf-8');
        console.log('Новая конфигурация загружена');
    }
});

console.log('Мониторинг конфигураций запущен...');

Это уже низкоуровневое программирование на JavaScript в действии — мы взаимодействуем с системными событиями inotify (на Linux) через абстракцию Node.js.

Управление процессами: child_process

Настоящая мощь Node.js системного программирования раскрывается при работе с дочерними процессами. Вы можете запускать любые системные команды, обрабатывать их вывод и даже передавать данные через pipe.

Запуск внешней команды и получение вывода

const { exec } = require('child_process');
const util = require('util');

const execPromise = util.promisify(exec);

async function getSystemInfo() {
    try {
        // Получаем информацию о дисках
        const { stdout: dfOutput } = await execPromise('df -h');
        console.log('Информация о дисках:');
        console.log(dfOutput);
        
        // Получаем список процессов
        const { stdout: psOutput } = await execPromise('ps aux --sort=-%mem | head -10');
        console.log('Топ процессов по памяти:');
        console.log(psOutput);
        
        // Сетевая статистика
        const { stdout: netOutput } = await execPromise('ss -tuln');
        console.log('Открытые порты:');
        console.log(netOutput);
    } catch (error) {
        console.error('Ошибка выполнения команды:', error.stderr);
    }
}

getSystemInfo();

Обратите внимание: мы используем promisify, чтобы избежать callback hell. Это стандартная практика для JS для администрирования.

Создание фонового процесса с pipe

const { spawn } = require('child_process');

function monitorCPULoad() {
    // Запускаем top в пакетном режиме
    const top = spawn('top', ['-b', '-n', '1']);
    const grep = spawn('grep', ['Cpu(s)']);
    
    top.stdout.pipe(grep.stdin);
    
    let result = '';
    grep.stdout.on('data', (data) => {
        result += data.toString();
    });
    
    grep.on('close', (code) => {
        if (code === 0) {
            const cpuLoad = result.match(/(\d+\.\d+)%? id/);
            if (cpuLoad) {
                const idle = parseFloat(cpuLoad[1]);
                const used = 100 - idle;
                console.log(`Загрузка CPU: ${used.toFixed(1)}%`);
                
                if (used > 80) {
                    console.warn('Внимание! Высокая загрузка CPU!');
                }
            }
        }
    });
}

// Запускаем мониторинг каждые 5 секунд
setInterval(monitorCPULoad, 5000);

Этот пример демонстрирует, как JavaScript для системного программирования может заменить классические bash-скрипты. Мы используем pipe между процессами точно так же, как в Unix shell, но с дополнительными возможностями логики на JS.

Сетевое программирование: создание простого монитора

Сеть — еще одна область, где системное ПО на JavaScript чувствует себя как рыба в воде. Встроенный модуль net позволяет создавать TCP-клиенты и серверы.

                

Блоги

Рекомендаций по книгам