我正试图让spawn
实现rm -rf node_modules
,然后是npm install
(在windows 7上;nx命令由透明安装的CygWin提供。所有nx命令都可以在命令行上解析)。
我最初使用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,似乎表明这正是一个人应该如何使用产卵。。。
在尝试了很多不同的东西之后,我终于看到了什么"npm";实际上是在windows上,它是一个名为npm
的bash脚本,以及一个称为npm.cmd
的windows本机批处理脚本(不知道为什么是.cmd,应该是.bat,但现在已经有了)。Windows的命令解析程序将看到npm
,注意到它不是可执行文件,请看到npm.cmd
,然后注意到IS是可执行文件。当您在终端中时,这很有帮助,但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脚本,无论是否使用通配符。
在最初的问题中,由于我试图以exec/spown的身份运行npm
以进行清理和重新安装,因此引发了错误,现代的解决方案是在package.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!");
});
(当然,也可以作为package.js中的复合脚本,例如"redo": "run-s clean install"
,然后使用runAll(["redo"])
)
我认为这可能是某种cygwin gotcha。我正在运行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。
对流程使用完整路径,如:
var cmd = require('child_process').spawn("C:\windows\system32\cmd.exe");