child_process/vm 模块的使用,执行 js 代码,执行其他程序
exec 方法
方法签名 : exec(string command[,object option],function callback)
- 第一个参数接受一个 command,会以一个新 shell 打开,在 win 上是 cmd.exe,不是新窗口
- 第二个参数 option,这个参数是可选的
见
也就是
参数名称 | 描述 |
---|---|
cwd | 表示新创建进程的工作目录(current working directory) |
env | process.env 未指定的话,从 parent process 继承 |
encoding | stdout/stderr 的 encoding,默认 utf8 |
timeout | 超时 |
maxBuffer | 最大 Buffer 空间,默认 200KB |
killSingal | 发送给 child_process 的信号,默认为 SIGTERM |
- 第 3 个参数,
callback = function(err,stdout,stderr){ //blabla... }
- err 错误
- stdout/stderr 不是 Stream,而是 string,因为这个 callback 是在 child_process 退出后执行的,存放的是过程中的输出
execFile
方法签名 :
1 | execFile( |
基本与 exec 方法相同
区别
- 第一个参数,execFile 只能是文件名,exec 包含执行的所有参数,
例如exec("git add --all")
execFile("git",["add","--all"])
参数由后面一个数组指定 - execFile 不会新建 shell,exec 会,在 win 上就是 cmd.exe,execFile 不会打开新窗口,而在当前窗口执行
注意 : windows 中文系统,cmd Console 的 encoding 是 gbk/gb2312 之类的,而在 execFile 的 option 参数写上{ encoding : 'gbk'}
提示错误,不能识别此种编码
解决办法是:使用 iconv-lite 转码
- 写上
encoding:'binary'
,我的理解就是一个个字节数组什么的,没有应用编码 - 在 callback 里面解码,也就是将字节数组组合成一个字符,例如 python2 里面的 Unicode 与 str 类型,Unicode 类型就是字节数组,没组合过的,只是取用的是 2 个
1
2
3
4function(err,stdout,stderr){
var ch_str = require('iconv-lite').decode(stdout,"gbk");
console.log(ch_str);//可以显示中文
}
spawn
英文是vi,vt 产卵,大量生产
,这个方法最为灵活,能实现很多功能
签名 : spawn(string file_mame,[] args,object options)
签名与 execFile 一致,除了没有 callback 参数
option 与(exec、execFile)的 option 不尽相同
也就是
参数名称 | 描述 |
---|---|
cwd | 工作目录 |
env | process.env |
- | |
stdio | [] or string 表示 standard stream |
detached | bool 表示是否是一个进程组(process group)的 leader,如果是,那么父进程退出后,这个还能运行,默认 false |
uid | number 类型,user identity,默认 null |
gid | number 类型,group id,默认 null |
stdio 参数 : 可以为一个 string,或者一个数组
取值[stdin,stdout,stderr]
pipe,与 parent process 之间创建管道
inherit,使用父 process 的 stream
ipc 通讯,不了解…
如果取值只有一个 string,可以扩展成 3 个
并且 string 与 stream 之间可以混用,如stdio : [null,process.stdout,"inherit"]
使用 inherit 可以很方便地对其他文件进行包装,如在程序中使用 git
1 | var cp = require("child_process"); |
ChildProcess 类
cp.spawn 不接受 callback,返回 ChildProcess 实例
除了在调用的时候指定 stdio 的来源,也可以之后对 ChildProcess 实例进行控制
如
1 | var ls = cp.spawn("ls"); |
事件
- error,当 spawn 没有成功 or killed or IPC 通讯消息没有成功发送时
- exit,退出时,
callback(code,singal)
- close,当标准流被关闭时,
callback(code,singal)
属性 pid,child.pid
表示进程 id
方法 kill(string singal),关闭 child process
fork 方法
spawn 的特殊形式,创建 node 子进程
签名 : cp.fork(modulePath, [args], [options])
option 有 cwd/env/encoding 可以配置
生成的子进程,与父进程,通过 send()/on(“message”,function(message){})通信
1 | child.on("message", function(msg) { |
VM 模块
vm.runInThisContext(string code)
,当前上下文,不能访问局部变量vm.runInNewContext(string code,object sandbox)
,sandbox 沙盒,global 被重置vm.runInContext(string code,object context)
,context,通过 vm.createContext 创建vm.createScript(string code)
方法签名省略了一个参数,string virtual_file_name,表示此段代码的虚拟文件名,此文件名只是为了在错误的 Stack Trace 能清楚一点,不会映射到__dirname
,__filename
全局变量
1 | runInThisContext("console.log(__dirname);", "/virtual/dir/file.ext"); |
runInThisContext
在当前上下文运行,可以访问全局变量 global variable,与 eval 函数的区别是,runInThisContext 不能访问局部变量 local variable,eval is evil 的原因就是这个,eval 可以访问局部变量并改写
例子
1 | var vm = require("vm"); |
1 | var vm = require("vm"); |
1 | var vm = require("vm"); |
可以看到 eval 可以访问 var foo 这种局部变量并改写,而 runInThisContext 只能访问全局变量,相对安全
runInNewContext
上下文是一个沙盒环境 sandbox,数据独立,看例子
1 | var vm = require("vm"); |
复制代码到 shell 可查看结果
在runInNewContext这句输出了两个,"Hello vm"和绿色的"Goodbye"
,是 shell 会自动将最后一个值 inspect 出来,code 里面最后一个赋值语句问题
总结就是:runInNewContext 创造一个 sandbox 作为新的 global 变量,里面连 console 都不能访问而要自己传递给新的 global 变量,这个 sandbox 会持有 var bar = 1 这样的局部变量数据
runInContext
runInContext 与 runInNewContext 的区别 : 前者的第二个参数是一个 context 对象,后者是 sandbox
1 | var vm = require("vm"); |
对于测试结果,将 runInNewContext 的 sandbox 改为 context 后,输出结果一样,runInContext 就是要手动 createContext
createSCript
编译 code 成为 vm.Script 实例
实例方法,对应于上面 vm 模块的 3 个 static 方法
- script.runInThisContext()
- script.runInNewContext([sandbox])
- script.runInContext(context)