問題解決 child_process execSync /bin/sh: pnpm: command not found
Context
我在執行 child_process.execSync 時,使用了下方程式碼去跑 db migration 並且指定了環境變數 DATABASE_URL 去執行 schema refresh & seed
execSync('pnpm -F backend db:reset && pnpm -F backend seed', {
env: { DATABASE_URL: databaseUrl },
})本人是使用 Windows 作業系統,這行指令在我電腦中執行的很順暢,所以就開開心心的把這行程式推上 Github 了。
不料沒多久,一位使用 Mac 的同事就對我說這行程式沒辦法跑啊,一直寫說 pnpm command not found
我循思這不太可能啊,每個人都有裝了 pnpm 才對,為何這時候又冒出了這個錯誤?不會又是 Mac 的鍋吧...😂

Problem
我不斷地在網路上衝浪,直到找到這篇 Stack Overflow,裡面有一段程式碼可以幫助我們 debug
const { execSync } = require('child_process')
console.log(process.env.ComSpec);// verify what terminal is used
try {
// success command
const resNpmVersion = execSync("npm -v");
console.log("success", resNpmVersion.toString());
// failed command, the result is printed in the catch block
const resHelp = execSync("HELP");
} catch (error) {
console.log(error.message);
console.log("error", error.stdout.toString());
}於是我把這段程式碼交給他,說道你把 npm 改成 pnpm 看看能不能執行?
過了一段時間後,他說:可以耶!我的版本是 9.x.x ...
我驚訝到,為什麼這樣可以執行??
Solution
於是我看了一下差異問道
是不是指定環境變數的問題呢?把 process.env 也放進去會如何呢?
execSync('pnpm -F backend db:reset && pnpm -F backend seed', {
env: { ...process.env, DATABASE_URL: databaseUrl },
})it's works!果真就能夠執行了!
後來找到了一篇文章 使用child_process时注意环境变量 寫道:
呼叫child_process.execSync時,若指定了env,切記一定要把繼承來的process.env帶上。 否則最重要的PATH環境變數就可能這麼被忽略了,導致子 Process 要呼叫 shell 指令時,發生各種找不到命令的奇怪錯誤。
官方文件中也寫道:
The command lookup is performed using theoptions.env.PATHenvironment variable ifenvis in theoptionsobject. Otherwise,process.env.PATHis used. Ifoptions.envis set withoutPATH, lookup on Unix is performed on a default search path search of/usr/bin:/bin(see your operating system's manual for execvpe/execvp), on Windows the current processes environment variablePATHis used. On Windows, environment variables are case-insensitive. Node.js lexicographically sorts theenvkeys and uses the first one that case-insensitively matches. Only first (in lexicographic order) entry will be passed to the subprocess. This might lead to issues on Windows when passing objects to theenvoption that have multiple variants of the same key, such asPATHandPath.
結論:下參數之前要先讀 spec 啊...