【问题标题】:Using nodejs's spawn causes "unknown option -- " and "[Error: spawn ENOENT]" errors使用 nodejs 的 spawn 会导致“unknown option --”和“[Error: spawn ENOENT]”错误
【发布时间】:2013-07-05 04:52:25
【问题描述】:

我正在尝试让spawn 影响rm -rf node_modules,然后是npm install(在 Windows 7 上;nx 命令由透明安装的 CygWin 提供。所有 n x 命令在命令行上解析就好了)。

我最初使用exec,但想在它发生时捕获stdout/stderr信息,所以我想我会使用spawn,并重写了代码来使用它。但是,这会破坏一切。

rm 命令重写后变成了这样:

var spawn = require("child_process").spawn,
    child = spawn("rm", ["-rf", "node_modules"]);
child.stdout.on('data', function (data) { console.log(data.toString()); });
child.stderr.on('data', function (data) { console.log(data.toString()); });
child.on('error', function() { console.log(arguments); });

但是,运行它会产生以下错误:

rm: unknown option -- ,

Try `rm --help' for more information.

npm 命令重写后变成了这样:

var spawn = require("child_process").spawn,
    child = spawn("npm", ["install"]);
child.stdout.on('data', function (data) { console.log(data.toString()); });
child.stderr.on('data', function (data) { console.log(data.toString()); });
child.on('error', function() { console.log(arguments); });

但是,运行它会产生以下错误:

{
  '0': {
    [Error: spawn ENOENT]
    code: 'ENOENT',
    errno: 'ENOENT',
    syscall: 'spawn'
  }
}

如何让 spawn 运行使用 exec 运行良好的相同命令,而不会到处抛出错误?为什么这不起作用?阅读 API,http://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options,似乎表明这正是人们应该如何使用 spawn...

【问题讨论】:

    标签: node.js exec spawn


    【解决方案1】:

    在尝试了很多不同的东西之后,我终于看到了 Windows 上的“npm”实际上是什么,结果发现它是一个名为 npm 的 bash 脚本,以及一个名为 @ 的 Windows 原生批处理脚本987654324@(不知道为什么是 .cmd,应该是 .bat,但你有它)。 Windows 的命令解析器将看到npm,注意到它不是可执行文件,参见npm.cmd,然后注意到它是可执行文件,然后将使用它。当您在终端中时,这很有帮助,但spawn() 不会执行任何此类解析:传递它npm 将使其失败,因为它不是可执行文件。但是,将 npm.cmd 作为命令传递它就可以了。

    (另外,不知道为什么 rm 早先失败了,因为它实际上可以正常工作而没有任何我可以说的更改。可能误读为问题的一部分,而实际上并非如此。)

    所以:如果您在 Windows 中遇到 spawn 说 ENOENT,当您尝试触发的命令在普通命令提示符下运行时,请确定您正在调用的命令是否是真正的可执行文件,或者是否有一个.bat/.cmd 文件,命令提示符将为您“帮助”运行。如果是这样,请生成它。

    编辑

    由于这篇文章仍在获得支持,确保命令始终有效的一个好方法是基于 process.platform 引导它,对于 Windows 将是 win32。

    var npm = (process.platform === "win32" ? "npm.cmd" : "npm"),
        child = spawn(npm, ["install", ...]);
    ...
    

    针对触发此错误的用例进行编辑

    自从发布这个问题(及其答案)以来,已经发布了几个包,这些包允许运行 npm 任务而不必依赖 exec 或 spawn,您应该改用它们。

    可能最受欢迎的是npm-run-all,它不仅可以让您从其他 npm 脚本以及 Node 运行任何 npm 任务,还可以添加命令以串联或依次运行多个 npm 脚本并行,带或不带通配符。

    在原始问题的上下文中,由于我试图将 npm 作为 exec/spawn 运行以进行清理和重新安装,因此引发了错误,现代解决方案是在包.json:

    {
      ...
      "scripts": {
        "clean": "rimraf ./node_modules",
        ...
      },
      ...
    }
    

    然后调用clean 任务,然后在命令行上执行安装命令

    > npm run clean && npm install
    

    或者,从某个 Node 脚本内部,使用:

    const runAll = require("npm-run-all");
    ...
    runAll(["clean", "install"])
        .then(() => {
            console.log("done!");
        })
        .catch(err => {
            console.log("failed!");
        });
    

    【讨论】:

    • 一个.cmd 文件是almost the same thing 但不完全是。
    • 您可以使用exec("npm install ..."),它实际上适用于spawn 失败的窗口。尽管exec 不会为您提供实时标准输出/标准错误数据 (AFAIK)。
    • 不,我不能;正如您所指出的, exec 没有 stdout 和 stderr 处理,没有日志的 npm install 非常没用。
    • 尝试在 Windows 上执行child = spawn('cmd', ['/c', 'rm -rf node_modules'], {env:process.env});。
    • @Mike'Pomax'Kamermans,这非常有用,让我难倒了好几个小时。太糟糕了,npm 的人决定故意不支持 API...
    【解决方案2】:

    我认为这可能是某种 cygwin 陷阱。我正在运行 Ubuntu 12.04 并试图复制您的问题,但它对我来说效果很好。简而言之,我看不出你做错了什么。

    如果它抱怨该选项,可以将其拆分为多个选项,如下所示:

    child = spawn("rm", ["-r", "-f", "node_modules"]);
    

    这简直是万岁,但这也适用于我的 Ubuntu 12.04。您可能会尝试只删除一个文件,看看是否得到相同的结果。

    child = spawn("rm", ["/home/username/Desktop/TestFile"]);
    

    如果仍然失败,那么你就知道你正在与一些疯狂的东西作斗争。

    您甚至可以尝试只执行不带参数的命令:

    child = spawn("ls");
    

    如果仍然失败,我猜你根本不可能让 spawn 工作,我很感激至少 exec 工作正常。

    在你的答案范围内没有太多,但就像我说的,我看不出你做错了什么。

    此外,我看不到您的 npm 命令将如何工作,因为您没有指定要安装的内容,但话虽如此,它失败的方式与我在这里看到的失败方式不同,如果我使用相同的命令。 . .我看到很多 stderr 输出,而不是整体错误。

    顺便说一句,我正在运行节点 v0.8.21。您可以通过 node -v 查询。如果您正在运行其他版本,不妨试试 0.8.21。

    【讨论】:

    • 原来它比这更有趣。添加了解释实际情况的答案。
    • 我没有运行 cygwin,所以我不确定它是什么可执行文件,但是 exec 或 spawn 需要先运行 cygwin,然后 cygwin 运行 'rm'。
    【解决方案3】:

    使用进程的完整路径,例如:

    var cmd = require('child_process').spawn("C:\\windows\\system32\\cmd.exe");
    

    【讨论】:

    • 只有在您已经知道可执行文件的扩展名和位置时才有效。对它们进行硬编码会立即阻止您的项目在其他人的计算机上运行,​​更不用说跨平台工作了。
    猜你喜欢
    • 2022-10-06
    • 2021-06-30
    • 2014-11-13
    • 2015-10-02
    • 1970-01-01
    • 2015-12-15
    • 1970-01-01
    • 2014-10-23
    相关资源
    最近更新 更多