如何在node.js上调试“错误:生成ENOENT”?


350

就我而言,我像将字符串一样传递整个命令,exec而不是将命令作为第一个参数传递而将选项作为第二个参数的数组传递。例如我在spawn( "adb logcat -c" )代替spawn( "adb", [ "logcat", "-c" ] )
Joshua Pinter

Answers:


235

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

我找到了一种了解以下根本原因的简便方法:

Error: spawn ENOENT

该错误的问题是,错误消息中实际上没有什么信息可以告诉您调用站点在哪里,即找不到哪个可执行文件/命令,尤其是当您有大量代码库且其中有很多派生调用时。另一方面,如果我们知道导致错误的确切命令,则可以按照@laconbass的回答来解决问题。

我发现了一种非常简单的方法来找出导致该问题的命令,而不是像@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无法找到可执行文件来解决该问题。


3
这是另一个想法:只需更改spawn()为,exec()然后重试即可。exec()会告诉您它试图运行什么命令。
亚当·蒙森

1
重要提示:请确保将上面的代码放置在尽可能靠近主JS文件开头的位置。如果您首先加载其他模块,则它们可以隐藏“ spawn”功能,并且此处的覆盖将永远不会被调用。
Dan Nissenbaum 2015年

1
我没有使用脚本的运气。它根本不起作用。
newguy

那么,您将如何在grunt文件中使用此方法?我不确定该放在哪里。
菲利克斯·夏娃

2
这对我来说非常有效。我只是将其放在gulpfile.js文件的顶部,然后使用bingo bango bongo,生成日志记录!
亚恩·杜兰

121

步骤1:确保spawn方法正确

首先,查看child_process.spawn(command,args,options)文档

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

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

{ cwd: undefined, env: process.env }

使用env指定的环境变量,这将是新的过程可见,默认为process.env

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

步骤2:确定发出错误事件的事件发射器

在您的源代码中搜索对的每次调用 spawn或的child_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. 你是明确地传递一个env对象spawnoptions参数。

在这两种情况下,您都必须检查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

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

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

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

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

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

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

缺少安装的程序是找不到命令的最常见原因。如果需要,请参考每个命令文档并进行安装。

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

一旦确定PATH正确设置并且command可以从中访问,就应该能够生成子进程而不会spawn ENOENT被抛出。


1
这对我调试Spawn ENOENT很有帮助。我已经多次引用了它。谢谢!
CodeManiak

36
我还发现,如果您cwd在选项中指定,将抛出ENOENT ,但是给定目录不存在。
Daniel Imfeld

4
@DanielImfeld总救助。您应该写一个这样的答案。
GreenAsJade

4
当您使用spawn('some-command', ['--help'], { env: env });此答案中第3步所举例说明的方法并通过自定义环境时,请确保指定PATH,例如:{ env: { PATH: process.env.PATH } }。默认情况下,env选项不会从当前环境中继承变量。
2016年

5
通过传递shell: true到生成选项,我能够解决我的问题。
Nickofthyme,


29

Windows解决方案:替换spawnnode-cross-spawn。例如在您的app.js开头这样的例子:

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

2
有用,除了它是一个插件,不需要child_process。与节点的spawn或spawnSync完全相同,因此替代品有所减少。var spawn = require('cross-spawn'); // Spawn NPM asynchronously var child = spawn('npm', ['list', '-g', '-depth', '0'], { stdio: 'inherit' });
博格丹·特鲁斯卡

27

@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
});

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


1
考虑重构我的答案以提供“一般指南”​​,并为问题的每个原因(缺少依赖项,错误的调用,错误的环境等)留下详细信息。
laconbass 2016年

2
每个喜欢此答案的人都可能对这种本机替代方案感兴趣:gist.github.com/ORESoftware/7bf225f0045b4649de6848f1ea5def4c
Alexander Mills

1
投反对票的原因是如果您要使用的是shell,那么您应该使用child_process.exec或传递shell: truespawn
givanse

@givanse不一定正确-您可能要运行zsh或bash或fsh,具体取决于要使用的shell,而且行为也有所不同
Alexander Mills

22

对于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})

1
这些修改在哪里进行?
Deilan

8
关键部分是添加shell: true
Ted Nyberg '18

19

对于任何人谁可能在此跌倒,如果所有其他的答案没有帮助,你是在Windows上,知道的是,目前的一个大问题与spawn在WindowsPATHEXT环境变量,可能会导致某些呼叫产卵不工作,这取决于如何目标命令已安装。


2
那有什么解决方案?
尼尔佐

6
使用node-cross-spawn对我有用。请参见下面的答案:stackoverflow.com/a/35561971/507339
Nilzor

1
花了很长时间试图找到问题所在,这最终成为问题。我放弃了spawn,只是exec改用了。
2016年

8

就我而言,由于未安装必要的依赖系统资源而引发此错误。

更具体地说,我有一个使用ImageMagick的NodeJS应用程序。尽管已安装了npm软件包,但尚未安装核心Linux ImageMagick。我做了一个易于安装ImageMagick的工作,之后一切都变好了!


Windows是否也需要安装ImageMagick?我在Windows上测试并得到错误
某人的名字,

6

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

错误的:

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

正确:

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

5

您要更改env选项吗?

然后看看这个答案。


我试图生成一个节点进程和TIL,当您生成节点进程和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',
  }
});

4

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

安装:

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

yarn install --frozen-lockfile

要么

npm ci

尊重地 如果没有

yarn install

要么

npm i

哇,这么简单的解决方案对我有用!每个人都应该先尝试一下,看看是否可以解决问题。
尼克K

2

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

要解决此问题,您可以使用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']);

2

使用require('child_process').exec而不是生成更具体的错误消息!

例如:

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);
});

1

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


1

我在运行测试用例时也遇到了这个烦人的问题,因此我尝试了多种方法来解决它。但是对我来说有效的方法是从包含主文件的目录运行测试运行程序,该主文件包含您的nodejs生成函数,如下所示:

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

例如,此文件名为test.js,因此只需移至包含该文件的文件夹。就我而言,它是这样的测试文件夹:

cd root/test/

然后在我的情况下,从运行您的测试运行程序的摩卡程序开始,它将像这样:

mocha test.js

我已经浪费了超过一天的时间来解决这个问题。请享用!!


1

我在Windows上遇到了这个问题,在Windows上调用execspawn使用完全相同的命令(省略参数)可以很好地工作exec(因此我知道我的命令在$PATH),但是spawn会给出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']);

0

尝试从Debian Linux系统上的VS Code编辑器中调试node.js程序时遇到此错误。我注意到同一件事在Windows上也可以。之前在此给出的解决方案并没有太大帮助,因为我没有编写任何“生成”命令。有问题的代码大概是由Microsoft编写的,并隐藏在VS Code程序的内部。

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

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

这样就解决了问题。在您的node.js被称为nodejs的其他情况下,相同或相似的过程可能会起作用,但是您正在运行一个期望将其称为node的程序,反之亦然。


0

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

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

但失败了。有一个windowsVerbatimArguments处理引号/类似内容的极好的未公开文档的选项似乎可以解决问题,只需确保将以下内容添加到您的opts对象中:

const opts = {
    windowsVerbatimArguments: true
};

并且您的命令应该重新使用。

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

不要在数组中引用参数
laconbass

@laconbass这是传达概念的简单例子,因此可以删除引号。但是,在某些情况下,您绝对需要用引号引起来(例如,如果您需要传递带有路径中带有空格的参数:“ C:\ Program Files \ ...”)。我将其发布在这里是因为,即使它可能不是造成您特定错误的原因,但由于Node在Windows上像我所遇到的那样处理引号,它有望帮助其他人遇到这种神秘的错误。
Joel B

node.js已经制作了一些Black Magic,并且默默地“正确”引用了参数。通过取消引用数组内的参数,您的示例应在没有提到的未记录选项的情况下工作。
laconbass

只是为了增加自己的经验,我正在从node运行Java进程。发生此错误的原因是命令周围的引号而不是参数。在命令路径中使用空格进行测试,它仍然可以不带引号
-Troncoso

0

就我而言

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');

1
尽管这可能是针对特定胜利的解决方案,但我看不出它如何帮助调试ENOENT的真正原因
laconbass

我不知道为什么,但是在没有的情况下,spawn调用将在节点repl中工作.cmd,但是在打字稿开玩笑测试中失败。-这个错误很难弄清楚,这个答案值得更多支持。
Mathieu CAROFF

0

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


0

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


1
同样,相关文件系统路径也以某种方式存在。没有管理员权限,扩展名可能无法到达路径
laconbass

-1

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


-2

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

脚步

  1. 转到我的电脑和属性

  2. 点击高级设置

  3. 然后关于环境变量

  4. 选择Path,然后单击编辑

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

  6. 关闭命令提示符

  7. 运行您要运行的命令

Windows 8环境变量屏幕截图


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.