process.argv は、Node.jsのプログラムを起動したコマンドラインを入れた配列です。インデックス0は node 実行ファイルのパス、インデックス1はスクリプトのパスで、自分の引数はインデックス2から始まります。つまり process.argv.slice(2) で、ユーザーがファイル名の後に入力したものがそのまま手に入ります。値は常に文字列です。
最初の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' ]
クォートで囲むと複数の単語が1つの引数にまとまり("Ada Lovelace" は1つの文字列として届きます)、*.txt のようなパターンはNode.jsに届く前にシェルがファイル名へ展開します。
slice(2)で引数を取り出す
たいていのスクリプトは、期待する引数を分割代入で受け取り、それぞれにデフォルト値を与えます。
Hello, Ada が表示されました。引数1つが name に入り、greeting はデフォルト値になりました。引数の欄で Ada の後に Hi を足すと Hi, Ada と表示され、欄を空にすると Hello, world になります。
数値は文字列として届く
シェルには型がありません。引数はすべて文字列なので、変換するまでは計算がおかしくなります。
a + b は文字列をつないで 75 になり、Number() を使うと 12 になりました。数値でない単語は NaN になるので、Number.isNaN で確認し、壊れた値のまま進まずに使い方のメッセージを表示しましょう。
--name=valueフラグを手でパースする
フラグが2つ3つなら、短いループで十分です。このループは --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 です。文字列は1文字ずつ比較されるからです。先に変換しましょう。
引数に秘密情報を入れる。 コマンドラインはシェルの履歴や、マシン上のほかのユーザーも見られるプロセス一覧に残ります。パスワードやAPIキーは代わりに環境変数で渡してください。
よくある質問
npmスクリプトに引数を渡すには?
-- の後に書きます。npm run start -- --port=4000 のようにすると、npmは -- より後ろをすべてスクリプトに渡し、それが process.argv.slice(2) に現れます。
process.argv[2]がundefinedになるのはなぜですか?
その引数なしでプログラムを起動したからです。デフォルト値を与える(process.argv[2] ?? 'guest')か、使い方を表示して process.exit(1) で終了しましょう。
process.argvの代わりにyargsやcommanderを使うべきですか?
フラグが数個のスクリプトなら、組み込みの util.parseArgs で十分です。commanderやyargsのようなパッケージはヘルプ表示、サブコマンド、バリデーションを追加してくれるので、ツールにコマンドが複数あるなら元が取れます。