LoginSignup
2
0

More than 1 year has passed since last update.

Node.jsでCLIの引数を解析する方法、node:util.parseArgsのご紹介も!

Posted at

はいさい!ちゅらデータぬオースティンやいびーん!

概要

CLIで実行するNode.jsのアプリケーションを実行する時に、CLIからアプリケーションに引数を渡して、そしてその渡された引数をアプリケーションで解析する方法を紹介します。

必要な知識

この記事を理解するためには、Node.jsを使ってCLIでJavaScriptを実行する知識が必要です。

参考になった記事

process.argvを使い、Node.jsアプリケーションでCLI引数を読む

Node.jsのアプリケーションには、processのグローバル変数があり、この変数には、環境変数を始めとして、さまざまな便利な情報にアクセスすることができます。

今回、CLIの引数にアクセスするためには、process.argvを使います。

以下のようなJavaScriptを書いて、

index.js
#!/usr/bin/env node
console.log(process.argv);

そしてNode.jsで実行してみますと、

node index.js --user churataro

以下の配列が出力されます。

[
  '/usr/local/bin/node',
  '/Users/austin.mayer/Documents/practice/apply-saml-creds/test.js',
  '--user',
  'churataro'
]

文字列の配列なのです。そして、指定もしていない謎の文字列がIndex 0とIndex 1にきているのではないでしょうか。

この二つの文字列は何なのでしょうか?

Index 0の'/usr/local/bin/node'

これは、このindex.jsを実行するのに使ったNode.jsのバイナリーのローカルパスです。つまり、ローカルにインストールしてあるNode.jsがどこにインストールされているかを教えてくれています。

Index 1の'/Users/austin.mayer/Documents/practice/apply-saml-creds/index.js'

これは、index.jsのローカルパスです。つまり、index.jsを実行している時のNode.jsのPATHになります。

Index 2以降

Index 2以降の値は、CLIで渡された引数になります。空白で分けてあります。

この部分の引数を解析したいので、通常、以下のようにIndex 2までの引数を無視して使います。

index.js
#!/usr/bin/env node
const args = process.argv.slice(2);
console.log(args);

キーと値でCLI引数を解析するロジックを作成する

次、CLI引数をキーと値にまとめてアプリケーションで使えるようにするためのロジックを書いていきましょう。

ここからはTypeScriptで書きますので、tscを実行して、コンパイルされたものを実行しましょう。

今回のアプリケーションでは--your-flag FLAG1という形式で引数をCLIで指定する前提でロジックを作成します。

以下の関数をご覧ください。

typescript:index.ts
const convertArgumentsToMap = (providedArguments: string[]) => {
  const argumentsMap = new Map<string, string | boolean>();
  const keyRegex = /--[a-z]{1,}-?[a-z]{1,}/;
  for (let index = 0; index < providedArguments.length; index += 2) {
    const key = providedArguments[index];
    if (!key) break;
    if (!keyRegex.test(key)) throw Error(`Invalid key provided: "${key}". Keys must be provided with two hyphens.`);
    const value = providedArguments[index + 1];
    argumentsMap.set(key, value ?? true);
  }

  return argumentsMap;
};

まず、const argumentsMap = new Map<string, string | boolean>();で引数のキーと値を保管するMapを作ります。

それから、--flag-name FLAG-VALUEという形式を取っているので、キーの--flag-nameのように正しい形式で指定してもらっているかどうかを打診するために、正規表現の/--[a-z]{1,}-?[a-z]{1,}/を使います。

ちなみに、--flagのように、2番目のハイフンは任意なので、正規表現で?を使っています。本来だったら、回帰的な処理ができるような書き方をしたいところですが、JavaScriptではその正規表現ができないらしいです。

次に、forループという、パッとしない手段で、一回の処理で配列の二つの値を引っ張り出しています。

一つ目の値はキーになるはずなので、それに対しては正規表現のテストを実行して正しい形式になっているかどうかを確かめます。

その上、配列の次の値も抽出して、その値があるかどうかをチェックした上で、argumentsMapに入れておきます。値がなかったら、boolean系のFlagだということでtrueを入れます。

CLI引数配列の最後までくると、argumentMapを返して終わり!

試しに引数を渡してみる

ここで、上記書いた関数を使って、CLI引数を解析してみます。

index.js
#!/usr/bin/env node

const convertArgumentsToMap = (providedArguments) => {
  const argumentsMap = new Map();
  const keyRegex = /--[a-z]{1,}-?[a-z]{1,}/;
  for (let index = 0; index < providedArguments.length; index += 2) {
    const key = providedArguments[index];
    if (!key) break;
    if (!keyRegex.test(key)) throw Error(`Invalid key provided: "${key}". Keys must be provided with two hyphens.`);
    const value = providedArguments[index + 1];
    argumentsMap.set(key, value ?? true);
  }

  return argumentsMap;
};

const args = process.argv.slice(2);
const argsMap = convertArgumentsToMap(args);

console.log(argsMap);

上記のスクリプトを以下のCLI引数で実行してみます。

node test.js --user churataro --password churadata

すると、Mapが以下のように出力されます。

Map(2) { '--user' => 'churataro', '--password' => 'churadata' }

試しに、正しくない形式で引数を渡して実行してみます。

node test.js user churataro --password churadata

すると以下のようにエラーが表示されます。

Error: Invalid key provided: "user". Keys must be provided with two hyphens.

Node.jsのnode:utils.parseArgsを使う

注意 こちらは、試験的な機能で、最新のNode.js 18.8でしか使えない!

最新のNode.jsには、もっと合理的な引数を解析してくれるツールが入っています。それはnode:utils.parseArgsです。

以下、parseArgsの使い方を説明してまいります。

parseArgsの引数について

parseArgsを実行する時に、引数にオブジェクトを渡します。

その中の必須な設定を説明します。

options

こちらは、CLI引数でどのような引数を受け入れて使うかを設定するものです。

こちらもオブジェクトになりますが、以下のような型になっています。

type Option = {
  type: "boolean" | "string"; // 必須
  short?: string; // 任意、省略フラグを定義するのに使う
  multiple?: boolean; // 任意、複数回指定していいかどうか
};

このtype Optionからなるオブジェクトなので、以下のような書き方ができます。

const options: Record<string, Option> = {
  "account-number": {
    type: "string",
    short: "a",
    multiple: false,
  },
  role: {
    type: "string",
    short: "r",
    multiple: false,
  },
};

こうして指定したoptionsは後ほどparseArgsで使います。

args

こちらは、process.argvから取るCLI引数です。

const args = process.argv.slice(2);

使ってみる

上記の情報を使って実行してみます。

index.js
#!/usr/bin/env node
const { parseArgs } = require("node:util");

const options = {
  "account-number": {
    type: "string",
    short: "a",
    multiple: false,
  },
  role: {
    type: "string",
    short: "r",
    multiple: false,
  },
};

const args = process.argv.slice(2);

const parsedArgs = parseArgs({ options, args });

console.log(parsedArgs);

以下のようなコマンドで実行してみましょう。

node test.js --account-number 100 --role Admin

結果:

{
  values: [Object: null prototype] { 'account-number': '100', role: 'Admin' },
  positionals: []
}

ちゃんと、account-numberroleが取れていますね!
*positionalsについては今回の記事で説明しません。気になる方は公式ドキュメントをご参照ください。

上記のoptionsで任意のshortも設定しているのですが、shortを設定するとCLIでは以下のような引数の渡し方ができます。

node test.js -a 100 -r Admin

また、設定されていない引数を渡すと、以下のようなエラーが出ます。

node test.js --account-number 100 --role Admin --unknown

結果:

TypeError [ERR_PARSE_ARGS_UNKNOWN_OPTION]: Unknown option '--unknown'
    at new NodeError (node:internal/errors:393:5)
    at checkOptionUsage (node:internal/util/parse_args/parse_args:97:11)
    at node:internal/util/parse_args/parse_args:327:9
    at Array.forEach (<anonymous>)
    at parseArgs (node:internal/util/parse_args/parse_args:324:3)
    at Object.<anonymous> (/Users/austin.mayer/Documents/practice/apply-saml-creds/test.js:19:20)
    at Module._compile (node:internal/modules/cjs/loader:1119:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1173:10)
    at Module.load (node:internal/modules/cjs/loader:997:32)
    at Module._load (node:internal/modules/cjs/loader:838:12) {
  code: 'ERR_PARSE_ARGS_UNKNOWN_OPTION'
}

自作のコードより便利なので、使いたい!と思いますが、まだ試験的な段階なので、完全に推奨できません。今後の楽しみにしておきましょう!

まとめ

以上、Node.jsでCLI引数を解析する方法を紹介してまいりましたが、いかがでしょうか?

最後に紹介したparseArgsが待ち遠しいですね。

今回、CLI引数を解析する方法について調べるきっかけは、筆者が初めてnpmに公開したパッケージで使うからです。

そのパッケージは、AWSにSAMLでログインした場合、そのSAML Responseを使って、ローカルの.aws/credentialsファイルにも反映させるスクリプトです。

筆者は、毎回手書きでやるのがしににりーだったので、Nodeでやったら、面白いはず!と思い、またさらに、npmに公開する方法もついでに勉強したらいいはず!とも思い、随分楽しい経験になりました。

実際、誰1人として使うとは到底思いませんが!(笑)

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0