🎉

ElectronからSpawnで外部コマンド(CLI)を呼び出すのに失敗して困った話

2022/03/15に公開約1,400字

概要

Electron から CLI で何かを呼び出し、その結果をElectron 内部で使用するパターンってあるかと思います。

会社のプロジェクトでも ffmpeg 呼び出したりすることもあり、別プロセスになるので Electron 側の処理が重くなることもなくいい感じですよね。

そんな外部呼び出しがデバッグ環境からは問題なく動くのに、Packaged 環境だと ENOENT とか exit code 127 とかでエラーになって起動できないことがありました。

原因

呼び出そうとしていた CLI/usr/local/bin にのみ入っており、他にエイリアスが貼られているわけでも無い状態でした。

VSCode からのデバッグ時には process.env.PATH に通常のターミナルと同じものが入っていたのでパスが通っている、Packaged だとこの PATH が反映されておらず起動しないという流れでした(半日ハマってました 😆)

動くようになった

child_process.spawnenvPATH を加えてやるとうまくいきました。

import { spawn } from 'child_process

const child = spawn('CLI名', ['arg1', 'arg2'], { env: { ...process.env, PATH: `${process.env.PATH}:/usr/local/bin` } });

~~~

こんな感じでオプションで env をいじってやることで CLI へのパスが通り、うまく動きました。

こんなことしないで CLI のフルパス書いたらいいじゃん!って思うのですが、PATH を追加していない状態でフルパスで呼んでみたら今度は exit code 127 が帰ってきてしまったので迷走してしまった感じです 🤗

もしかすると呼び出される側の CLI にもパイプ関連などで何かあったのかも?調査しきれていませんゴメンナサイ 😅

解決

このままでも動くようになったのですが、ターミナル環境と同じ PATH を通したかったのでモジュールで解決しました。

https://github.com/sindresorhus/shell-path

こいつを呼ぶことでターミナル環境と同じ PATH を取得できるので、spawn にそのまま渡してやれば OK ですね。

ちなみに使い方は

const shellPath = require('shell-path').sync();

もしくは

onst shellPath = require('shell-path');

~~
const path = await shellPath();
~~

という感じで Promise も返してくれるのでお好きな方で。

Typescript の定義があればよかったんですがなかったので require して使っています。

あとがき

わかってみれば難しい問題でもないのですがハマりだすと大変ですよね、とくに JS 界隈は様々なモジュールが絡み合ってるので僕みたいなライトプログラマーはハマりポイントが多いです 💦

GitHubで編集を提案

Discussion

ログインするとコメントできます