process.argv это массив с командной строкой, которой запустили вашу программу на Node.js. Индекс 0 это путь к исполняемому файлу node, индекс 1 это путь к вашему скрипту, а ваши собственные аргументы начинаются с индекса 2. Поэтому process.argv.slice(2) даёт ровно то, что пользователь ввёл после имени файла, и всегда в виде строк.
Первые два элемента это полные пути, поэтому программа выводит только их последнюю часть (node и index.js), а затем [ 'Ada', '42', '--verbose' ]. Измените строку аргументов на Grace 7 и запустите снова: массив поменяется следом.
Что попадает в process.argv
Оболочка делит строку после node по пробелам и передаёт Node.js полученные части. Флаги, записанные до имени файла, относятся к самому Node.js, а не к вашей программе:
node --max-old-space-size=4096 index.js --port=8080
# process.argv.slice(2) is [ '--port=8080' ]
# process.execArgv is [ '--max-old-space-size=4096' ]
Кавычки объединяют слова в один аргумент ("Ada Lovelace" приходит одной строкой), а шаблоны вроде *.txt оболочка раскрывает в имена файлов ещё до того, как их увидит Node.js.
Аргументы через slice(2)
Большинство скриптов деструктурируют ожидаемые аргументы и дают каждому значение по умолчанию:
Программа вывела Hello, Ada: один аргумент заполнил name, а greeting взял значение по умолчанию. Добавьте Hi после Ada в строке аргументов, и она выведет Hi, Ada; очистите строку, и получите Hello, world.
Числа приходят строками
У оболочки нет типов. Каждый аргумент это строка, поэтому арифметика с ними ломается, пока вы их не преобразуете:
a + b склеил строки в 75, а Number() дал 12. Слово, которое не является числом, превращается в NaN, поэтому проверяйте через Number.isNaN и выводите подсказку по использованию, а не работайте дальше со сломанным значением.
Разбор флагов --name=value вручную
Для пары флагов хватит короткого цикла. Этот превращает --port=8080 в port: '8080', одиночный --verbose в true, а всё остальное оставляет позиционными аргументами:
Программа вывела {"port":"8080","verbose":true} и [ 'build', 'src' ], причём port остался строкой. Этот цикл не понимает короткие флаги вроде -p и значение в следующем аргументе (--port 8080); для этого используйте встроенный парсер ниже.
util.parseArgs, встроенный парсер
В Node.js 18.3 и новее есть util.parseArgs, который понимает длинные и короткие флаги, значения в следующем аргументе, булевы флаги и значения по умолчанию:
const { parseArgs } = require('node:util');
// node index.js -p 8080 --verbose src
const { values, positionals } = parseArgs({
options: {
port: { type: 'string', short: 'p', default: '3000' },
verbose: { type: 'boolean', short: 'v' },
},
allowPositionals: true,
});
console.log(values); // [Object: null prototype] { port: '8080', verbose: true }
console.log(positionals); // [ 'src' ]
По умолчанию parseArgs читает process.argv.slice(2). Он бросает ошибку на опции, которую вы не объявили, и так ловит опечатки вроде --prot; передайте strict: false, чтобы принимать неизвестные флаги. Типы опций бывают только string и boolean, так что числам всё равно нужен Number(). values это объект без прототипа, поэтому он выводится с [Object: null prototype]; читайте его как обычный объект.
Частые ошибки
Считать process.argv[0] первым аргументом. Это путь к node. Ваш первый аргумент это process.argv[2].
Забыть, что всё это строки. '10' > '9' равно false, потому что строки сравниваются посимвольно. Сначала преобразуйте.
Передавать секреты в аргументах. Командные строки попадают в историю оболочки и в списки процессов, которые видят другие пользователи машины. Пароли и API ключи передавайте через переменные окружения.
Часто задаваемые вопросы
Как передать аргументы в npm скрипт?
Напишите их после --: npm run start -- --port=4000. Всё, что идёт после --, npm передаёт вашему скрипту, и оно появляется в process.argv.slice(2).
Почему process.argv[2] равен undefined?
Потому что программу запустили без этого аргумента. Задайте значение по умолчанию (process.argv[2] ?? 'guest') или выведите подсказку по использованию и остановитесь через process.exit(1).
Стоит ли брать yargs или commander вместо process.argv?
Для скрипта с парой флагов хватит встроенного util.parseArgs. Пакеты вроде commander и yargs добавляют справку, подкоманды и валидацию, и это окупается, когда у инструмента несколько команд.