【问题标题】:How do I debug "Error: spawn ENOENT" on node.js?如何在 node.js 上调试“错误:生成 ENOENT”?
【发布时间】:2015-02-25 15:15:08
【问题描述】:

【问题讨论】:

  • 在我的例子中,我将整个命令作为字符串传递,就像你使用 exec 一样,而不是将命令作为第一个参数传递,将选项作为第二个参数的数组传递。例如我在做spawn( "adb logcat -c" ) 而不是spawn( "adb", [ "logcat", "-c" ] )

标签: node.js debugging error-handling child-process spawn


【解决方案1】:

第 1 步:确保以正确的方式调用 spawn

首先,查看docs for child_process.spawn( command, args, options )

使用给定的command 启动一个新进程,命令行参数在args 中。如果省略,args 默认为空数组。

第三个参数用于指定附加选项,默认为:

{ cwd: undefined, env: process.env }

使用env指定对新进程可见的环境变量,默认为process.env

确保您没有在 command 中放入任何命令行参数,并且整个 spawn 调用有效。继续下一步。

第 2 步:识别发出错误事件的 Event Emitter

在您的源代码中搜索对spawnchild_process.spawn 的每次调用,即

spawn('some-command', [ '--help' ]);

并为“错误”事件附加一个事件侦听器,这样您就会注意到将它作为“未处理”抛出的确切事件发射器。调试后,可以删除该处理程序。

spawn('some-command', [ '--help' ])
  .on('error', function( err ){ throw err })
;

执行,您应该获得注册“错误”侦听器的文件路径和行号。比如:

/file/that/registers/the/error/listener.js:29
      throw err;
            ^
Error: spawn ENOENT
    at errnoException (child_process.js:1000:11)
    at Process.ChildProcess._handle.onexit (child_process.js:791:34)

如果前两行还在

events.js:72
        throw er; // Unhandled 'error' event

再次执行此步骤,直到不再执行此步骤。 在继续下一步之前,您必须确定发出错误的侦听器。

第 3 步:确保设置了环境变量 $PATH

有两种可能的情况:

  1. 您依赖默认的spawn 行为,因此子进程环境将与process.env 相同。
  2. 您在 options 参数上明确将 env 对象传递给 spawn

在这两种情况下,您都必须检查生成的子进程将使用的环境对象上的 PATH 键。

场景 1 的示例

// inspect the PATH key on process.env
console.log( process.env.PATH );
spawn('some-command', ['--help']);

场景 2 的示例

var env = getEnvKeyValuePairsSomeHow();
// inspect the PATH key on the env object
console.log( env.PATH );
spawn('some-command', ['--help'], { env: env });

缺少PATH(即undefined)将导致spawn 发出ENOENT 错误,因为无法找到任何command,除非它是可执行文件的绝对路径。

PATH 设置正确后,继续下一步。 它应该是一个目录,或者是一个目录列表。最后一种情况是正常的。

步骤 4:确保command 存在于PATH 中定义的目录中

如果在PATH 上定义的至少一个目录中不存在文件名command(即'some-command'),Spawn 可能会发出ENOENT 错误。

找到command 的确切位置。在大多数 linux 发行版上,这可以从终端使用 which 命令完成。它会告诉你可执行文件的绝对路径(如上),或者告诉你是否找不到。

找到命令时的示例用法及其输出

> which some-command
some-command is /usr/bin/some-command

找不到命令时的示例用法及其输出

> which some-command
bash: type: some-command: not found

未找到安装程序是找不到命令的最常见原因。如果需要,请参阅每个命令文档并安装它。

如果命令是一个简单的脚本文件,请确保它可以从 PATH 上的目录访问。如果不是,请将其移至一个或创建一个链接。

一旦您确定PATH 设置正确并且可以从中访问command,您应该能够在不抛出spawn ENOENT 的情况下生成您的子进程。

【讨论】:

  • 这对我调试 Spawn ENOENT 很有帮助。我已经多次引用它。谢谢!
  • 我还发现如果在选项中指定cwd会抛出ENOENT,但是给定的目录不存在。
  • @DanielImfeld 总救星。你应该写一个这样的答案。
  • 当您使用spawn('some-command', ['--help'], { env: env });(如本答案中的第 3 步所示)并传递自定义环境时,请务必指定 PATH,例如:{ env: { PATH: process.env.PATH } }。默认情况下,env 选项不会从当前环境中继承变量。
  • 我能够通过将shell: true 传递给生成选项来解决我的问题。
【解决方案2】:

注意:此错误几乎总是由于命令不存在、工作目录不存在或仅 Windows 的错误引起的。

我找到了一种特别简单的方法来了解以下问题的根本原因:

Error: spawn ENOENT

这个错误的问题是,错误消息中很少有信息告诉你调用站点在哪里,即没有找到哪个可执行文件/命令,特别是当你有一个很大的代码库时产卵电话。另一方面,如果我们知道导致错误的确切命令,那么我们可以关注@laconbass' answer 来解决问题。

我找到了一种非常简单的方法来找出导致问题的命令,而不是按照@laconbass 的回答中的建议在代码中的任何地方添加事件侦听器。关键思想是使用包装器包装原始 spawn 调用,该包装器打印发送到 spawn 调用的参数。

这里是包装函数,把它放在index.js 或任何你的服务器的启动脚本的顶部。

(function() {
    var childProcess = require("child_process");
    var oldSpawn = childProcess.spawn;
    function mySpawn() {
        console.log('spawn called');
        console.log(arguments);
        var result = oldSpawn.apply(this, arguments);
        return result;
    }
    childProcess.spawn = mySpawn;
})();

然后下次运行应用程序时,在未捕获的异常消息之前,您会看到类似的内容:

spawn called
{ '0': 'hg',
  '1': [],
  '2':
   { cwd: '/* omitted */',
     env: { IP: '0.0.0.0' },
     args: [] } }

这样你就可以很容易的知道实际执行了哪个命令,然后你就可以找出nodejs为什么找不到可执行文件来解决问题了。

【讨论】:

  • 这是另一个想法:只需将 spawn() 更改为 exec() 并重试。 exec() 会告诉你它尝试运行什么命令。
  • 重要提示:确保将上面的代码放在尽可能靠近主 JS 文件的开头。如果您先加载其他模块,它们可以隐藏“spawn”函数,并且永远不会调用这里的覆盖。
  • 我没有运气使用该脚本。它根本不起作用。
  • 那么您将如何在 grunt 文件中使用此方法?我不知道把这个放在哪里。
  • 这对我来说非常有效。我只是把它放在我的 gulpfile.js 文件的顶部,然后 bingo bango bongo,生成日志记录!
【解决方案3】:

在我的情况下,由于未安装必要的依赖系统资源,我收到此错误。

更具体地说,我有一个使用 ImageMagick 的 NodeJS 应用程序。尽管安装了 npm 包,但核心 Linux ImageMagick 并未安装。我做了一个 apt-get 来安装 ImageMagick,之后一切都很好!

【讨论】:

  • Windows 是否也需要安装 ImageMagick?我在 Windows 上测试并出现错误
  • 运行yarn(安装)修复它
  • 简单运行brew install imagemagick
【解决方案4】:

对于任何可能偶然发现这一点的人,如果所有其他答案都没有帮助并且您在 Windows 上,请知道当前有 a big issue with spawn on WindowsPATHEXT 环境变量可能导致某些调用产生不起作用取决于目标命令的安装方式。

【讨论】:

  • 解决办法是什么?
  • 使用 node-cross-spawn 对我有用。请参阅下面的答案:stackoverflow.com/a/35561971/507339
  • 花了很长时间试图找出问题所在,而这最终成为了问题所在。我放弃了spawn,改用exec
【解决方案5】:

Windows 解决方案:将spawn 替换为node-cross-spawn。例如在你的 app.js 开头这样:

(function() {
    var childProcess = require("child_process");
    childProcess.spawn = require('cross-spawn');
})(); 

【讨论】:

  • 工作,除了它是一个插件,不需要 child_process。与节点的 spawn 或 spawnSync 完全相同,因此它是替代品。 var spawn = require('cross-spawn');// Spawn NPM asynchronously var child = spawn('npm', ['list', '-g', '-depth', '0'], { stdio: 'inherit' });
【解决方案6】:

我在尝试从 Debian Linux 系统上的 VS Code 编辑器中调试 node.js 程序时遇到此错误。我注意到同样的事情在 Windows 上运行良好。之前在这里给出的解决方案没有多大帮助,因为我没有编写任何“spawn”命令。有问题的代码可能是由微软编写的,并隐藏在 VS Code 程序的底层。

接下来,我注意到 node.js 在 Windows 上称为 node,但在 Debian(可能在基于 Debian 的系统,如 Ubuntu)上称为 nodejs。所以我创建了一个别名 - 从一个根终端,我跑了

ln -s /usr/bin/nodejs /usr/local/bin/node

这解决了问题。相同或类似的过程可能适用于其他情况,即您的 node.js 被称为 nodejs,但您正在运行的程序期望它被称为 node,反之亦然。

【讨论】:

    【解决方案7】:

    @laconbass 的回答帮助了我,而且可能是最正确的。

    我来这里是因为我错误地使用了 spawn。 举个简单的例子:

    这是不正确的:

    const s = cp.spawn('npm install -D suman', [], {
        cwd: root
    });
    

    这是不正确的:

    const s = cp.spawn('npm', ['install -D suman'], {
        cwd: root
    });
    

    这是正确的:

    const s = cp.spawn('npm', ['install','-D','suman'], {
        cwd: root
    });
    

    但是,我建议这样做:

    const s = cp.spawn('bash');
    s.stdin.end(`cd "${root}" && npm install -D suman`);
    s.once('exit', code => {
       // exit
    });
    

    这是因为只要安装了 bash,cp.on('exit', fn) 事件就会一直触发,否则,如果我们以第一种方式使用它,如果我们直接启动“npm”,cp.on('error', fn) 事件可能会首先触发。

    【讨论】:

    • 考虑重构我的答案以提供“一般指南”​​,并为每个问题原因留下详细信息(缺少依赖关系、错误调用、错误环境......)。
    • 每个喜欢这个答案的人,可能也对这个原生替代品感兴趣:gist.github.com/ORESoftware/7bf225f0045b4649de6848f1ea5def4c
    • 投反对票,因为如果您想要的是一个外壳,那么您应该使用child_process.exec 或将shell: true 传递给spawn
    • @givanse 不一定正确 - 您可能需要运行 zsh 或 bash 或 fsh,具体取决于您要使用的 shell,并且行为也不同
    【解决方案8】:

    我在 Windows 8 上遇到了同样的错误。问题是因为缺少系统路径的环境变量。将“C:\Windows\System32\”值添加到系统 PATH 变量中。

    【讨论】:

      【解决方案9】:

      我遇到了同样的问题,但我找到了一个简单的方法来解决它。 如果程序已被用户添加到 PATH 中(例如正常的系统命令有效),则似乎是 spawn() 错误。

      要解决此问题,您可以使用 which 模块 (npm install --save which):

      // Require which and child_process
      const which = require('which');
      const spawn = require('child_process').spawn;
      // Find npm in PATH
      const npm = which.sync('npm');
      // Execute
      const noErrorSpawn = spawn(npm, ['install']);
      

      【讨论】:

        【解决方案10】:

        作为@DanielImfeld pointed it,如果在选项中指定“cwd”,则将抛出ENOENT,但给定目录不存在。

        【讨论】:

        • 那么有没有办法在特定目录中执行命令?
        • 在 Windows (7) 中,您似乎还需要在 cwd 路径中包含驱动器号:'c:/...' 而不仅仅是 '/...'
        【解决方案11】:

        C:\Windows\System32\ 添加到path 环境变量中。

        步骤

        1. 转到我的计算机和属性

        2. 点击高级设置

        3. 然后是环境变量

        4. 选择Path然后点击编辑

        5. 如果不存在,请粘贴以下内容:C:\Windows\System32\

        6. 关闭命令提示符

        7. 运行你想运行的命令

        【讨论】:

        【解决方案12】:

        对于 Windows 上的 ENOENT,https://github.com/nodejs/node-v0.x-archive/issues/2318#issuecomment-249355505 修复它。

        例如将 spawn('npm', ['-v'], {stdio: 'inherit'}) 替换为:

        • 对于所有 node.js 版本:

          spawn(/^win/.test(process.platform) ? 'npm.cmd' : 'npm', ['-v'], {stdio: 'inherit'})
          
        • 对于 node.js 5.x 及更高版本:

          spawn('npm', ['-v'], {stdio: 'inherit', shell: true})
          

        【讨论】:

        • 在哪里进行这些修改?
        • 关键部分是添加shell: true
        【解决方案13】:

        确保要执行的模块已安装或命令的完整路径(如果它不是节点模块)

        【讨论】:

          【解决方案14】:

          使用require('child_process').exec 而不是 spawn 以获得更具体的错误消息!

          例如:

          var exec = require('child_process').exec;
          var commandStr = 'java -jar something.jar';
          
          exec(commandStr, function(error, stdout, stderr) {
            if(error || stderr) console.log(error || stderr);
            else console.log(stdout);
          });
          

          【讨论】:

            【解决方案15】:

            如果你在 Windows 上,Node.js 在处理引号时会做一些有趣的事情,这可能会导致你发出一个你知道可以从控制台运行的命令,但在 Node 中运行时却不行。例如以下应该工作:

            spawn('ping', ['"8.8.8.8"'], {});
            

            但失败了。有一个非常无证的选项windowsVerbatimArguments 用于处理引号/类似的东西似乎可以解决问题,只需确保将以下内容添加到您的 opts 对象:

            const opts = {
                windowsVerbatimArguments: true
            };
            

            你的指挥部应该恢复正常了。

             spawn('ping', ['"8.8.8.8"'], { windowsVerbatimArguments: true });
            

            【讨论】:

            • 不要引用数组内的参数
            • @laconbass 这显然是传达概念的微不足道的示例,因此可以删除引号。但是,在某些情况下,您绝对需要引用参数(例如,如果您需要传递一个路径中带有空格的参数:"C:\Program Files\...")。我在这里发布它是因为,即使它可能不是您的特定错误案例的原因,它也希望能帮助其他遇到这个神秘错误的人,因为 Node 在 Windows 上处理引号,就像我遇到的那样。
            • node.js 已经做出了一些黑魔法,并且默默地“正确”地引用了参数。通过在数组中取消引用参数,您的示例应该可以在没有您提到的未记录选项的情况下工作。
            • 只是为了添加我自己的经验,我正在从 node.js 运行一个 java 进程。这个错误发生在我身上是因为命令周围的引号,而不是参数。在命令路径中使用空格进行测试,它仍然可以在没有引号的情况下使用
            【解决方案16】:

            我在运行测试用例时也遇到了这个烦人的问题,所以我尝试了很多方法来解决它。但对我有用的方法是从包含您的主文件的目录中运行您的测试运行程序,其中包括您的 nodejs spawn 函数,如下所示:

            nodeProcess = spawn('node',params, {cwd: '../../node/', detached: true });
            

            例如,这个文件名是 test.js,所以只需移动到包含它的文件夹。就我而言,它是这样的测试文件夹:

            cd root/test/
            

            然后从 运行你的测试运行器,在我的例子中是 mocha,所以它会是这样的:

            mocha test.js
            

            我已经浪费了一天多的时间来弄清楚。尽情享受吧!

            【讨论】:

              【解决方案17】:

              我的解决方案

              var spawn = require('child_process').spawn;
              
              const isWindows = /^win/.test(process.platform); 
              
              spawn(isWindows ? 'twitter-proxy.cmd' : 'twitter-proxy');
              spawn(isWindows ? 'http-server.cmd' : 'http-server');
              

              【讨论】:

              • 虽然这可能是赢得特定修复的解决方案,但我看不出它如何有助于调试 ENOENT 的真正原因
              • 我不知道为什么,但是 spawn 调用可以在没有.cmd 的节点 repl 中工作,但在打字稿笑话测试中失败。 -- 这个错误可能很难弄清楚,这个答案值得更多的支持。
              • 现有答案已经涵盖了.cmd 扩展程序
              【解决方案18】:

              我在 Windows 上遇到了这个问题,使用完全相同的命令(省略参数)调用 execspawnexec 效果很好(所以我知道我的命令在 $PATH 上),但是 @ 987654325@ 会给 ENOENT。原来我只需要将.exe 附加到我正在使用的命令中:

              import { exec, spawn } from 'child_process';
              
              // This works fine
              exec('p4 changes -s submitted');
              
              // This gives the ENOENT error
              spawn('p4');
              
              // But this resolves it
              spawn('p4.exe');
              // Even works with the arguments now
              spawn('p4.exe', ['changes', '-s', 'submitted']);
              

              【讨论】:

                【解决方案19】:

                您要更改env 选项吗?

                然后看看这个答案。


                我试图生成一个节点进程和 TIL,当你生成时你应该传播现有的环境变量,否则你会丢失 PATH 环境变量和可能其他重要的环境变量。

                这是对我的修复:

                const nodeProcess = spawn('node', ['--help'], {
                  env: {
                    // by default, spawn uses `process.env` for the value of `env`
                    // you can _add_ to this behavior, by spreading `process.env`
                    ...process.env,
                    OTHER_ENV_VARIABLE: 'test',
                  }
                });
                

                【讨论】:

                  【解决方案20】:

                  如果您在使用无法修改其源的应用程序时遇到此问题,请考虑将环境变量 NODE_DEBUG 设置为 child_process 来调用它,例如NODE_DEBUG=child_process yarn test。这将为您提供在哪个目录中调用了哪些命令行的信息,通常最后一个细节是失败的原因。

                  【讨论】:

                  • 您知道能够使用此技术的最小节点 --version 吗?
                  • @laconbass 不。我认为这是一个很好的 SO 问题,将在数小时内得到答复。
                  • 只是好奇。这比我检查为接受的答案更好、更干净
                  【解决方案21】:

                  在 Windows 中,只需添加 shell: true 选项即可解决我的问题:

                  不正确:

                  const { spawn } = require('child_process');
                  const child = spawn('dir');
                  

                  正确:

                  const { spawn } = require('child_process');
                  const child = spawn('dir', [], {shell: true});
                  

                  【讨论】:

                  • 谢谢!这解决了我的问题,无需定义 cmd 或路径
                  【解决方案22】:

                  在任何人花费大量时间调试此问题之前,大多数情况下可以通过删除 node_modules 并重新安装软件包来解决。

                  安装:

                  如果存在锁定文件,您可以使用

                  yarn install --frozen-lockfile
                  

                  npm ci
                  

                  分别。如果不是那么

                  yarn install
                  

                  npm i
                  

                  【讨论】:

                  • 哇,这么简单的解决方案,它对我有用!每个人都应该先尝试一下,看看它是否能解决问题。
                  【解决方案23】:

                  尽管对某些人来说这可能是环境路径或其他问题,但我刚刚在 Windows 10 上安装了适用于 Visual Studio Code 的 Latex Workshop 扩展,并在尝试构建/预览 PDF 时看到了此错误。以管理员身份运行 VS Code 为我解决了这个问题。

                  【讨论】:

                  • 再次,以某种方式相关的文件系统路径。没有管理员权限,扩展程序可能无法到达路径
                  【解决方案24】:

                  如何研究引发错误的 spawn 调用:

                  已知的常见原因

                  1. 环境问题

                    • 系统中不存在命令可执行文件(未安装依赖项)。 prominc's answer
                    • PATH 环境变量指定的目录中不存在命令可执行文件。
                    • 可执行二进制文件是用不兼容的库编译的。 danilo-ramirez answer
                  2. 仅限 Windows 的错误/怪癖

                  3. spawn('command', ['--argument', 'list'], { cwd, env, ...opts }) 用法错误

                    • 指定的工作目录 (opts.cwd) 不存在·参见leeroy-brun's answer
                    • 命令String中的参数列表spawn('command --wrong --argument list')
                    • 命令字符串中的环境变量spawn('ENV_VAR=WRONG command')
                    • 参数列表Array指定为String spawn('cmd', '--argument list')
                    • 取消设置 PATH 环境变量 spawn('cmd', [], { env: { variable } } => spawn('cmd', [], { env: { ...process.env, variable } }

                  ENOENT 有 2 个可能的来源:

                  1. 您正在编写的代码
                  2. 你依赖的代码

                  当源代码是您依赖的代码时,通常原因是环境问题(或 windows 怪癖)


                  【讨论】:

                  • 我正在使用“ab”命令执行 execa,但是容器中没有安装“Apache Bench”……所以,第一个“环境问题”案例……
                  【解决方案25】:

                  在我的情况下删除节点,删除所有 AppData/Roaming/npm 和 AppData/Roaming/npm-cache 并再次安装节点解决问题。

                  【讨论】:

                    【解决方案26】:

                    最近我也遇到了类似的问题。

                    Starting the development server...
                    
                    events.js:174
                          throw er; // Unhandled 'error' event
                          ^
                    
                    Error: spawn null ENOENT
                        at Process.ChildProcess._handle.onexit (internal/child_process.js:240:19)
                        at onErrorNT (internal/child_process.js:415:16)
                        at process._tickCallback (internal/process/next_tick.js:63:19)
                    Emitted 'error' event at:
                        at Process.ChildProcess._handle.onexit (internal/child_process.js:246:12)
                        at onErrorNT (internal/child_process.js:415:16)
                        at process._tickCallback (internal/process/next_tick.js:63:19)
                    error Command failed with exit code 1.
                    
                    

                    这是由于 .env 文件中的 BROWSER 配置错误。我有BROWSER=null,但它必须是BROWSER=none。更改该配置解决了我的问题。

                    【讨论】:

                      【解决方案27】:

                      什么都没试过,我的系统有不同的问题。

                      对我来说可行的解决方案是 运行命令: npm config set script-shell "C:\Program Files\git\bin\bash.exe"

                      【讨论】:

                      • 您的问题似乎与npm 本身有关,而不是与节点的child_process.spawn() 有关。似乎是一个 Windows 怪癖
                      【解决方案28】:

                      我发现的一个不在此列表中但值得添加的案例:

                      在 Alpine Linux 上,如果可执行文件不兼容,Node 将出错并显示 ENOENT。

                      Alpine 需要带有 libc 的二进制文件。已使用 glibc 作为系统调用的包装器编译的可执行文件(例如 chrome 作为 chromium 的一部分)在被 spawn 调用时将失败并显示 ENOENT。

                      【讨论】:

                      • 感谢您的提示
                      【解决方案29】:

                      模拟器本地开发

                      确保已在本地安装软件包。通过使用exec 更改spawn 命令,我得到了一个更详细的错误,并发现我没有安装该软件包。只需运行,检查包是否存在:

                      brew install imagemagick

                      Source

                      【讨论】:

                        猜你喜欢
                        • 2014-09-25
                        • 1970-01-01
                        • 1970-01-01
                        • 2017-08-31
                        • 2016-10-07
                        • 2015-03-12
                        • 2015-07-13
                        • 1970-01-01
                        相关资源
                        最近更新 更多