Как Node.js обрабатывает аргументы командной строки: от process.argv до сырцов

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

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

Начать курс

Как Node.js обрабатывает аргументы командной строки: от process.argv до сырцов

Представьте: вы написали крутой скрипт на Node.js, который конвертирует изображения. Но каждый раз править код, чтобы указать имя файла — это моветон. Гораздо круче запустить: node convert.js --input photo.jpg --format webp и получить результат. Как сделать такую магию? Именно этому вопросу, одному из самых популярных на Stack Overflow, мы сегодня и посвятим время.

Казалось бы, что может быть проще? Но под капотом Node.js (и сам V8) скрывает несколько слоёв, о которых полезно знать каждому разработчику. Мы не просто покажем, как получить process.argv — мы заглянем в исходники Node.js и узнаем, как аргументы путешествуют из вашего терминала в переменные окружения процесса. Поехали!

Зачем вообще нужны аргументы командной строки?

Аргументы командной строки (CLI arguments) — это основной способ сделать вашу программу гибкой и переиспользуемой без изменения кода. Вместо хардкода вы передаёте данные на вход. Это основа практически всех DevOps-инструментов, сборщиков (Webpack, Vite) и утилит. Умение работать с ними — базовый навык JavaScript-разработчика, который хочет выйти за рамки браузера.

Уровень 1: Основы — process.argv

Самый простой и нативный способ получить аргументы в Node.js — обратиться к глобальному объекту process.argv. Это массив строк. Давайте сразу на примере.

Файл: args-demo.js

// args-demo.js
console.log(process.argv);

Запустим с аргументами:

node args-demo.js hello --name=World 42

Вывод в консоль:

[
  '/usr/local/bin/node',   // 0: путь к исполняемому файлу Node.js
  '/home/user/args-demo.js', // 1: путь к вашему скрипту
  'hello',                 // 2: первый пользовательский аргумент
  '--name=World',          // 3: второй аргумент
  '42'                     // 4: третий аргумент
]

Вот и вся магия. process.argv — это просто срез того, что операционная система передала процессу. Первые два элемента — это служебная информация. Всё, что идёт после индекса 2 — это ваши аргументы. Важно: все они — строки. Число 42 пришло как строка "42".

Как отбросить первые два элемента?

Для удобства часто используют деструктуризацию:

// Получаем только пользовательские аргументы
const [, , ...userArgs] = process.argv;
console.log(userArgs); // ['hello', '--name=World', '42']

Или просто process.argv.slice(2).

Уровень 2: Парсим вручную — свой велосипед

Работать с сырым массивом строк неудобно. Нам нужно отличать флаги (--name) от значений (World). Давайте напишем простой парсер, чтобы понять логику.

// simple-parser.js
const args = process.argv.slice(2);
const parsed = {};

for (let i = 0; i < args.length; i++) {
  const arg = args[i];
  if (arg.startsWith('--')) {
    // Убираем '--' и ищем значение
    const key = arg.slice(2);
    const next = args[i + 1];
    // Если следующий аргумент не начинается с '--', это значение
    if (next && !next.startsWith('--')) {
      parsed[key] = next;
      i++; // Пропускаем следующий шаг итерации
    } else {
      parsed[key] = true; // Булевый флаг
    }
  } else if (arg.startsWith('-')) {
    // Короткие флаги, например -v
    const key = arg.slice(1);
    parsed[key] = true;
  } else {
    // Просто позиционный аргумент
    if (!parsed._) {
      parsed._ = [];
    }
    parsed._.push(arg);
  }
}

console.log(parsed);
// node simple-parser.js --name World -v hello
// { name: 'World', v: true, _: ['hello'] }

Этот код — основа библиотек вроде minimist. Как видите, ничего сложного. Но в реальных проектах лучше использовать готовые решения, чтобы не изобретать велосипед и не обрабатывать краевые случаи (например, флаги с пробелами: --name "John Doe").

Уровень 3: Под капотом — как Node.js получает argv?

А теперь самое интересное. Как вообще process.argv появляется в нашей программе? Откуда берутся эти строки?

Когда вы запускаете node script.js arg1, ваша оболочка (bash, zsh, cmd) вызывает системный вызов execve() (или его аналог в Windows). Этот вызов передаёт ядру ОС путь к исполняемому файлу (Node.js) и массив строк-аргументов. Ядро создаёт новый процесс и передаёт ему этот массив.

Внутри Node.js (написан на C++ и JavaScript) есть слой инициализации. Когда процесс стартует, выполняется код на C++, который вызывает v8::V8::SetFlagsFromCommandLine (для флагов самого V8) и сохраняет оригинальный массив аргументов в глобальной переменной. Затем, на этапе загрузки JavaScript-окружения, этот массив копируется в свойство process.argv.

Кстати, если заглянуть в исходный код Node.js, можно найти функцию SetProcessArgv, которая как раз и отвечает за это преобразование. Она просто берёт сырой C-массив char** argv и конвертирует его в JavaScript-массив строк.

Важный нюанс: Node.js позволяет передавать флаги самому рантайму до имени скрипта. Например: node --inspect script.js. Эти флаги не попадают в process.argv для вашего скрипта, а обрабатываются до его запуска. Это сделано для того, чтобы скрипт не мог случайно переопределить важные настройки движка.

Уровень 4: Профессиональные решения — commander и yargs

Писать парсеры вручную для продакшена — плохая идея. Есть две главные библиотеки, которые используют все:

1. commander (популярна в CLI-инструментах)

Предоставляет декларативный API и автоматическую генерацию справки (--help).

// install: npm install commander
import { Command } from 'commander';
const program = new Command();

program
  .name('image-converter')
  .description('CLI to convert images')
  .version('1.0.0');

program
  .option('-i, --input <path>', 'input file path')
  .option('-f, --format <type>', '                

Блоги

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