【Node.js】process完全ガイド|環境変数・コマンドライン引数・終了コード

【Node.js】process完全ガイド|環境変数・コマンドライン引数・終了コード Node.js

processは、実行中のNode.jsプロセス自身の情報を持ち、操作するためのグローバルオブジェクトです。requireなしでどこからでも使えます。中でもよく使うのが、環境変数process.env)、コマンドライン引数process.argv)、終了コードprocess.exit)です。

つまずきやすいのは、環境変数の値が常に文字列になること(数字に見えても文字列)と、コマンドライン引数はargv[2]から始まること(先頭2つはnodeとスクリプトのパス)です。この記事では、実機のNode.jsで確認しながら、processの使い方を整理します。

先に結論

  • 環境変数はprocess.env.NAMEで読みます。設定されていなければundefinedです。
  • 環境変数の値はすべて文字列です。数値として使うならNumber()で変換します。
  • コマンドライン引数はprocess.argvの配列。[0]はnode、[1]はスクリプト、[2]以降が自分の引数です。
  • 自分の引数だけ取り出すならprocess.argv.slice(2)です。
  • 終了コードはprocess.exit(0)が成功、0以外が失敗。途中の処理を待つならprocess.exitCodeを使います。
  • 実行環境はprocess.platformwin32など)、process.cwd()(カレントフォルダ)で分かります。

環境変数はnpmのスクリプトや設定でよく使います。npmとpackage.jsonの基礎、サーバーのポート指定の例はhttpモジュールでWebサーバーを作る、グローバルの扱いはrequireとimportの違いもあわせて参考になります。

スポンサーリンク

process.envで環境変数を読む

環境変数はprocess.envから読みます。実行時にNAME=値 node app.jsのように渡すか、OSやデプロイ環境で設定された値を受け取ります。設定や秘密情報(APIキーなど)をコードに直接書かず、環境変数で渡すのが定番です。

env.js
// 環境変数を読む
console.log(process.env.NODE_ENV);   // production / development など
console.log(process.env.PORT);

// 設定されていなければ undefined。|| で既定値を用意するのが定番
const port = process.env.PORT || 3000;
const env = process.env.NODE_ENV || "development";
run-with-env.sh
# 実行時に環境変数を渡す(Linux / macOS)
PORT=8080 NODE_ENV=production node app.js

# Windows の PowerShell
$env:PORT=8080; node app.js

実機でも、PORT=3000 node ...で渡した値がprocess.env.PORTから読め、設定していない変数はundefined|| 8080で既定値になりました。多くのプロジェクトでは、.envファイルに環境変数をまとめ、dotenvパッケージで読み込む方法も使われます。

【重要】環境変数の値は常に文字列

見落としやすいのが、process.envの値は必ず文字列だということです。PORT=3000と渡しても、process.env.PORTは数値の3000ではなく文字列の"3000"です。

env-string.js
// PORT=3000 で実行した場合
console.log(typeof process.env.PORT);   // string(数値ではない!)
console.log(process.env.PORT + 1);      // "30001"(文字列の連結になる)

// 数値として使うなら Number() で変換する
const port = Number(process.env.PORT);
console.log(port + 1);   // 3001(正しく計算できる)

// 真偽値も文字列。"false" は真扱いになるので注意
const debug = process.env.DEBUG === "true";
数値・真偽値は自分で変換する

実機で確認したところ、PORT=3000で渡したprocess.env.PORTの型はstringでした。そのまま+ 1すると"30001"という文字列の連結になります。数値として使うならNumber(process.env.PORT)で変換してください。真偽値も同じで、process.env.DEBUGは文字列の"false"でも中身がある文字列なので真と扱われます。=== "true"のように明示的に比較する必要があります。

process.argvでコマンドライン引数を受け取る

コマンドラインから渡した引数はprocess.argvという配列で受け取れます。node script.js 引数1 引数2のように渡します。

argv.js
// node argv.js apple banana --flag で実行した場合
console.log(process.argv);
// [
//   "C:\\...\\node.exe",   // [0] node 本体のパス
//   "C:\\...\\argv.js",    // [1] スクリプトのパス
//   "apple",                   // [2] 最初の引数
//   "banana",                  // [3]
//   "--flag"                   // [4]
// ]

// 自分の引数だけ取り出す
const args = process.argv.slice(2);
console.log(args);   // ["apple", "banana", "--flag"]
argv[2]が最初の引数

実機で確認したところ、node argv.js apple bananaを実行すると、process.argv[0]はnode本体のパス、[1]はスクリプトのパス、[2]apple(最初の引数)、[3]bananaでした。自分が渡した引数は[2]から始まるため、process.argv.slice(2)で引数だけを取り出すのが定番です。「最初の引数をargv[0]で取ろうとしてnodeのパスが返る」というのはよくある間違いです。

引数を解析する(parseArgs)

--name 山田のようなオプション付きの引数を手で解析するのは面倒です。Node.js 18以降には、標準でutil.parseArgsが用意されており、オプションと値をきれいに分けられます。

parseargs.mjs
import { parseArgs } from "node:util";

const { values, positionals } = parseArgs({
    options: {
        name: { type: "string" },
        verbose: { type: "boolean" },
    },
    allowPositionals: true,
});

// 実行: node app.mjs --name 山田 --verbose file.txt
console.log(values);       // { name: "山田", verbose: true }
console.log(positionals);  // ["file.txt"]

実機でも、parseArgsでオプション(--name--verbose)と位置引数(file.txt)をきれいに分けられました。より高機能な解析が必要なら、commanderyargsといったパッケージもあります。簡単な用途なら、まず標準のparseArgsを試すとよいでしょう。

終了コード(process.exit / process.exitCode)

スクリプトの成否は終了コードで表します。0が成功、0以外が失敗です。シェルやCIは、この値で次の処理に進むかを判断します。

exit-code.js
// 終了コードを指定して即座に終了する
if (somethingFailed) {
    console.error("エラーが発生しました");
    process.exit(1);   // 1 = 失敗(0 以外なら失敗扱い)
}

// 正常終了(省略すると 0 になる)
process.exit(0);

// 注意: process.exit() は即座に終了するため、
// 書き込み中のファイルやログが途中で切れることがある。
// その場合は exitCode を設定して自然に終わらせる方が安全
process.exitCode = 1;
// (この後の処理は実行され、最後に終了コード 1 で終わる)
exit()は即終了、exitCodeは自然終了

実機で確認したところ、process.exit(3)はその場で終了し、シェルの終了コードは3になりました。一方、process.exitCode = 5を設定した場合は、その後の処理が最後まで実行されてから、終了コード5で終わりました。process.exit()は強制的に即終了するため、ファイルへの書き込みやログ出力が途中で切れることがあります。後始末が必要な処理では、process.exitCodeに値を入れて自然に終わらせるほうが安全です。

プロセスの情報(platform / version / cwd)

processからは、実行環境の情報も取れます。OSの判定や、現在のフォルダの取得などに使います。

process-info.js
console.log(process.platform);  // win32 / linux / darwin(macOS)
console.log(process.version);   // v20.19.0 など Node のバージョン
console.log(process.cwd());     // 実行したカレントフォルダ
console.log(process.pid);       // プロセスID(数値)

// OS によって処理を分ける例
if (process.platform === "win32") {
    console.log("Windows 環境です");
}

実機(Windows)では、process.platformwin32process.versionv20.19.0process.cwd()が実行フォルダ、process.pidは数値になりました。process.cwd()は「実行したフォルダ」を指すため、スクリプト自身の場所を基準にしたいときは、別物の__dirnameを使います(違いはpathモジュールの使い方で解説しています)。

標準出力とシグナル

画面への出力はconsole.logが手軽ですが、改行なしで出したいときなどはprocess.stdout.writeを使います。また、Ctrl+Cなどの「シグナル」を受け取って、終了前の後始末をすることもできます。

stdout-signal.js
// 改行なしで出力する(進捗表示など)
process.stdout.write("処理中");
process.stdout.write("...");
process.stdout.write("完了\n");

// Ctrl+C(SIGINT)を受け取って後始末してから終了する
process.on("SIGINT", () => {
    console.log("\n終了します。後始末中...");
    // ここでファイルのクローズなどを行う
    process.exit(0);
});

process.stdout.writeconsole.logと違って自動で改行されないため、進捗バーのような表示に向きます。process.on("SIGINT", ...)を使うと、Ctrl+Cで止められたときに、いきなり終了せず、開いているファイルや接続を閉じてから終了する、といった行儀のよい後始末ができます。

よくある失敗

環境変数を数値として計算してしまう

process.envの値は文字列です。process.env.PORT + 1は文字列連結になります。数値として使うならNumber()で変換します。

argv[0]やargv[1]を引数だと思う

argv[0]はnode、argv[1]はスクリプトのパスです。自分が渡した引数はargv[2]から。slice(2)で取り出します。

環境変数の “false” を偽だと思う

"false"は中身のある文字列なので、真として扱われます。真偽値はprocess.env.X === "true"のように明示的に比較します。

process.exit()でログが途中で切れる

process.exit()は即座に終了します。書き込み中の処理が切れることがあるため、後始末が必要ならprocess.exitCodeを使います。

0以外で正常終了したつもりになる

終了コードは0が成功、0以外は失敗です。CIなどでは失敗扱いになるため、正常時は0(または何も指定しない)にします。

よくある質問

Qprocess.env.PORTが数値になりません。
Aprocess.envの値はすべて文字列です。PORT=3000でもprocess.env.PORT"3000"です。数値として使うならNumber(process.env.PORT)で変換してください。
Qコマンドライン引数の最初はどれですか?
Aprocess.argv[2]です。[0]はnode本体、[1]はスクリプトのパスで、自分が渡した引数は[2]から始まります。process.argv.slice(2)で引数だけを取り出せます。
Qprocess.exitとexitCodeはどう違いますか?
Aprocess.exit(コード)はその場で即座に終了します。process.exitCode = コードは値を設定するだけで、その後の処理が最後まで実行されてから終了します。ファイル書き込みなどの後始末が必要なら、即終了しないexitCodeのほうが安全です。
Q環境変数を.envファイルで管理するには?
Adotenvパッケージが定番です。npm install dotenvで入れ、.envファイルにPORT=3000のように書き、コードの先頭でrequire("dotenv").config()を呼ぶとprocess.envから読めます。.envはGitにコミットしないよう注意します。
QOSによって処理を分けるには?
Aprocess.platformを見ます。Windowsはwin32、Linuxはlinux、macOSはdarwinです。if (process.platform === "win32")のように分岐できます。

まとめ

  • 環境変数はprocess.env.NAMEで読み、値は常に文字列。数値はNumber()で変換します。
  • コマンドライン引数はprocess.argv[2]以降が自分の引数で、slice(2)で取り出します。
  • オプション付き引数はutil.parseArgsできれいに解析できます。
  • 終了コードは0が成功。即終了はprocess.exit()、後始末を待つならprocess.exitCodeです。
  • 実行環境はprocess.platformprocess.versionprocess.cwd()で取得できます。
  • Ctrl+Cの後始末はprocess.on("SIGINT", ...)で行えます。

processは、設定の受け渡し(環境変数)、コマンドの引数、スクリプトの成否(終了コード)と、実用的なスクリプトを書くうえで欠かせない情報の入り口です。「envは文字列」「argvは2から」の2点を押さえれば、設定可能で再利用しやすいツールを作れるようになります。