io.js
通過child_process
模塊提供了三向的popen
功能。
可以無阻塞地通過子進程的stdin
,stdout
和stderr
以流的方式傳遞數(shù)據(jù)。(注意某些程序在內部使用了行緩沖I/O,這不會影響io.js
,但是這意味你傳遞給子進程的數(shù)據(jù)可能不會在第一時間被消費)。
可以通過require('child_process').spawn()
或require('child_process').fork()
創(chuàng)建子進程。這兩者間的語義有少許差別,將會在后面進行解釋。
當以寫腳本為目的時,你可以會覺得使用同步版本的方法會更方便。
ChildProcess
是一個EventEmitter
。
子進程總是有三個與之相關的流。child.stdin
,child.stdout
和child.stderr
。他們可能會共享父進程的stdio流,或者也可以是獨立的被導流的流對象。
ChildProcess
類并不是用來直接被使用的。應當使用spawn()
,exec()
,execFile()
或fork()
方法來創(chuàng)建一個子進程實例。
發(fā)生于:
進程不能被創(chuàng)建時,進程不能殺死時,給子進程發(fā)送信息失敗時。注意exit
事件在一個錯誤發(fā)生后可能觸發(fā)。如果你同時監(jiān)聽了這兩個事件來觸發(fā)一個函數(shù),需要記住不要讓這個函數(shù)被觸發(fā)兩次。
參閱 ChildProcess.kill()
和 ChildProcess.send()
。
注意子進程的stdio流可能仍為打開狀態(tài)。
還需要注意的是,io.js
已經(jīng)為我們添加了'SIGINT'信號和'SIGTERM'信號的事件處理函數(shù),所以在父進程發(fā)出這兩個信號時,進程將會退出。
參閱 waitpid(2)
。
stdio
流都關閉時觸發(fā)。這是與exit
的區(qū)別,因為可能會有幾個進程共享同樣的stdio
流。在父進程或子進程中使用.disconnect()
方法后這個事件會觸發(fā)。在斷開之后,將不能繼續(xù)相互發(fā)送信息,并且子進程的.connected
屬性將會是false
。
Socket
或Server
對象通過.send(message, [sendHandle])
發(fā)送的信息可以通過監(jiān)聽message
事件獲取到。
一個代表了子進程的stdin
的可寫流。通過end()
方法關閉此流可以終止子進程。
如果子進程通過spawn
創(chuàng)建時stdio
沒有被設置為pipe
,那么它將不會被創(chuàng)建。
child.stdin
為child.stdio
中對應元素的快捷引用。它們要么都指向同一個對象,要么都為null。
一個代表了子進程的stdout
的可讀流。
如果子進程通過spawn
創(chuàng)建時stdio
沒有被設置為pipe
,那么它將不會被創(chuàng)建。
child.stdout
為child.stdio
中對應元素的快捷引用。它們要么都指向同一個對象,要么都為null。
一個代表了子進程的stderr
的可讀流。
如果子進程通過spawn
創(chuàng)建時stdio
沒有被設置為pipe
,那么它將不會被創(chuàng)建。
child.stderr
為child.stdio
中對應元素的快捷引用。它們要么都指向同一個對象,要么都為null。
一個包含了子進程的管道的稀疏數(shù)組,元素的位置對應著利用spawn
創(chuàng)建子進程時stdio
配置參數(shù)里被設置為pipe
的位置。注意索引為0-2的流分別與ChildProcess.stdin
, ChildProcess.stdout
和ChildProcess.stderr
引用的是相同的對象。
在下面的例子中,在stdio
參數(shù)中只有索引為1的元素被設置為了pipe
,所以父進程中只有child.stdio[1]
是一個流,其他的元素都為null
。
var assert = require('assert');
var fs = require('fs');
var child_process = require('child_process');
child = child_process.spawn('ls', {
stdio: [
0, // use parents stdin for child
'pipe', // pipe child's stdout to parent
fs.openSync('err.out', 'w') // direct child's stderr to a file
]
});
assert.equal(child.stdio[0], null);
assert.equal(child.stdio[0], child.stdin);
assert(child.stdout);
assert.equal(child.stdio[1], child.stdout);
assert.equal(child.stdio[2], null);
assert.equal(child.stdio[2], child.stderr);
子進程的PID
。
例子:
var spawn = require('child_process').spawn,
grep = spawn('grep', ['ssh']);
console.log('Spawned child pid: ' + grep.pid);
grep.stdin.end();
.disconnect
方法被調用后將會被設置為false
。如果.connected
屬性為false
,那么將不能再向子進程發(fā)送信息。給子進程傳遞一個信號。如果沒有指定任何參數(shù),那么將發(fā)送'SIGTERM'
給子進程。更多可用的信號請參閱signal(7)
。
var spawn = require('child_process').spawn,
grep = spawn('grep', ['ssh']);
grep.on('close', function (code, signal) {
console.log('child process terminated due to receipt of signal ' + signal);
});
// send SIGHUP to process
grep.kill('SIGHUP');
在信號不能被送達時,可能會產(chǎn)生一個error
事件。給一個已經(jīng)終止的子進程發(fā)送一個信號不會發(fā)生錯誤,但可以操作不可預料的后果:如果該子進程的PID
已經(jīng)被重新分配給了另一個進程,那么這個信號會被傳遞到另一個進程中。大家可以猜想這將會發(fā)生什么樣的情況。
注意這個函數(shù)僅僅是名字叫kill,給子進程發(fā)送的信號可能不是去關閉它的。這個函數(shù)僅僅只是給子進程發(fā)送一個信號。
參閱kill(2)
。
當使用child_process.fork()
時,你可以使用child.send(message, [sendHandle])
向子進程發(fā)送信息,子進程里會觸發(fā)message
事件當收到信息時。
例子:
var cp = require('child_process');
var n = cp.fork(__dirname + '/sub.js');
n.on('message', function(m) {
console.log('PARENT got message:', m);
});
n.send({ hello: 'world' });
子進程代碼, sub.js
可能看起來類似這樣:
process.on('message', function(m) {
console.log('CHILD got message:', m);
});
process.send({ foo: 'bar' });
在子進程中,process
對象將有一個send()
方法,在它的信道上收到一個信息時,信息將以對象的形式返回。
請注意父進程,子進程中的send()
方法都是同步的,所以發(fā)送大量數(shù)據(jù)是不被建議的(可以使用管道代替,參閱child_process.spawn
)。
發(fā)送{cmd: 'NODE_foo'}
信息時是一個特殊情況。所有的在cmd
屬性中包含了NODE_
前綴的信息都不會觸發(fā)message
事件,因為這是io.js
內核使用的內部信息。包含這個前綴的信息都會觸發(fā)internalMessage
事件。請避免使用這個事件,它在改變的時候不會收到通知。
child.send()
的sendHandle
參數(shù)時用來給另一個進程發(fā)送一個TCP服務器
或一個socket
的。將之作為第二個參數(shù)傳入,子進程將在message
事件中會收到這個對象。
如果信息不能被發(fā)送的話將會觸發(fā)一個error
事件,比如子進程已經(jīng)退出了。
例子:發(fā)送一個server
對象
var child = require('child_process').fork('child.js');
// Open up the server object and send the handle.
var server = require('net').createServer();
server.on('connection', function (socket) {
socket.end('handled by parent');
});
server.listen(1337, function() {
child.send('server', server);
});
子進程將會收到server
對象:
process.on('message', function(m, server) {
if (m === 'server') {
server.on('connection', function (socket) {
socket.end('handled by child');
});
}
});
注意這個server
現(xiàn)在已經(jīng)被父進程和子進程所共享,這意味著鏈接將可能被父進程處理也可能被子進程處理。
對于dgram
服務器,流程也是完全一樣的。使用message
事件而不是connection
事件,使用server.bind
問不是server.listen
(目前只支持UNIX
平臺)。
例子:發(fā)送一個socket
對象
以下是發(fā)送一個socket
的例子。創(chuàng)建了兩個子進程。并且將地址為74.125.127.100
的鏈接通過將socket
發(fā)送給"special"子進程來視作VIP。其他的socket
則被發(fā)送給"normal"子進程。
var normal = require('child_process').fork('child.js', ['normal']);
var special = require('child_process').fork('child.js', ['special']);
// Open up the server and send sockets to child
var server = require('net').createServer();
server.on('connection', function (socket) {
// if this is a VIP
if (socket.remoteAddress === '74.125.127.100') {
special.send('socket', socket);
return;
}
// just the usual dudes
normal.send('socket', socket);
});
server.listen(1337);
`child.js`:
```js
process.on('message', function(m, socket) {
if (m === 'socket') {
socket.end('You were handled as a ' + process.argv[2] + ' person');
}
});
注意一旦一個單獨的socket
被發(fā)送給了子進程,那么父進程將不能追蹤到這個socket
被刪除的時間,這個情況下.connections
屬性將會成為null
。在這個情況下同樣也不推薦使用.maxConnections
屬性。
關閉父進程與子進程間的IPC信道,它讓子進程非常優(yōu)雅地退出,因為已經(jīng)活躍的信道了。在調用了這個方法后,父進程和子進程的.connected
標簽都會被設置為false
,將不能再發(fā)送信息。
disconnect
事件在進程不再有消息接收時觸發(fā)。
注意,當子進程中有與父進程通信的IPC信道時,你也可以在子進程中調用process.disconnect()
。
以下方法遵循普遍的異步編程模式(接受一個回調函數(shù)或返回一個EventEmitter
)。
args Array 字符串參數(shù)數(shù)組
options Object
gid Number 設置進程組的ID
return: ChildProcess object
利用給定的命令以及參數(shù)執(zhí)行一個新的進程,如果沒有參數(shù)數(shù)組,那么args
將默認是一個空數(shù)組。
第三個參數(shù)時用來指定以為額外的配置,以下是它的默認值:
{ cwd: undefined,
env: process.env
}
使用cwd
來指定子進程的工作目錄。如果沒有指定,默認值是當前父進程的工作目錄。
使用env
來指定子進程中可用的環(huán)境變量,默認值是process.env
。
Example of running ls -lh /usr, capturing stdout, stderr, and the exit code:一個運行ls -lh /usr
,獲取stdout
,stderr
和退出碼得例子:
var spawn = require('child_process').spawn,
ls = spawn('ls', ['-lh', '/usr']);
ls.stdout.on('data', function (data) {
console.log('stdout: ' + data);
});
ls.stderr.on('data', function (data) {
console.log('stderr: ' + data);
});
ls.on('close', function (code) {
console.log('child process exited with code ' + code);
});
例子:一個非常精巧的運行ps ax | grep ssh
的方式
var spawn = require('child_process').spawn,
ps = spawn('ps', ['ax']),
grep = spawn('grep', ['ssh']);
ps.stdout.on('data', function (data) {
grep.stdin.write(data);
});
ps.stderr.on('data', function (data) {
console.log('ps stderr: ' + data);
});
ps.on('close', function (code) {
if (code !== 0) {
console.log('ps process exited with code ' + code);
}
grep.stdin.end();
});
grep.stdout.on('data', function (data) {
console.log('' + data);
});
grep.stderr.on('data', function (data) {
console.log('grep stderr: ' + data);
});
grep.on('close', function (code) {
if (code !== 0) {
console.log('grep process exited with code ' + code);
}
});
一個檢查執(zhí)行失敗的例子:
var spawn = require('child_process').spawn,
child = spawn('bad_command');
child.on('error', function (err) {
console.log('Failed to start child process.');
});
作為快捷方式,stdio
的值可以是一下字符串之一:
'pipe' - ['pipe', 'pipe', 'pipe'], 這是默認值'ignore' - ['ignore', 'ignore', 'ignore']'inherit' - [process.stdin, process.stdout, process.stderr]或[0,1,2]
否則,child_process.spawn()
的stdio
參數(shù)時一個數(shù)組,數(shù)組中的每一個索引的對應子進程中的一個文件標識符??梢允窍铝兄抵唬?/p>
'pipe' - 創(chuàng)建一個子進程與父進程之間的管道,管道的父進程端已父進程的child_process
對象的屬性(ChildProcess.stdio[fd]
)暴露給父進程。為文件表示(fds)0 - 2 創(chuàng)建的管道也可以通過ChildProcess.stdin
,ChildProcess.stdout
和ChildProcess.stderr
分別訪問。
'ipc' - 創(chuàng)建一個子進程和父進程間 傳輸信息/文件描述符 的IPC信道。一個子進程最多可能有一個IPC stdio 文件描述符。設置該選項將激活ChildProcess.send()
方法。如果子進程向此文件描述符中寫入JSON數(shù)據(jù),則會觸發(fā)ChildProcess.on('message')
。如果子進程是一個io.js
程序,那么IPC信道的存在將會激活process.send()
和process.on('message')
。
'ignore' - 不在子進程中設置文件描述符。注意io.js
總是會為通過spawn
創(chuàng)建的子進程打開文件描述符(fd) 0 - 2。如果這其中任意一項被設置為了ignore
,io.js
會打開/dev/null
并將其附給子進程對應的文件描述符(fd)。
Stream object - 與子進程共享一個與tty,文件,socket,或管道相關的可讀/可寫流。該流底層(underlying)的文件標識在子進程中被復制給stdio數(shù)組索引對應的文件描述符(fd)。
Positive integer - 該整形值被解釋為父進程中打開的文件標識符。他與子進程共享,和Stream被共享的方式相似。
null, undefined - 使用默認值。For 對于stdio fds 0,1,2(或者說stdin
,stdout
和stderr
),pipe管道被建立。對于fd 3及往后,默認為ignore
。
例子:
var spawn = require('child_process').spawn;
// Child will use parent's stdios
spawn('prg', [], { stdio: 'inherit' });
// Spawn child sharing only stderr
spawn('prg', [], { stdio: ['pipe', 'pipe', process.stderr] });
// Open an extra fd=4, to interact with programs present a
// startd-style interface.
spawn('prg', [], { stdio: ['pipe', null, null, null, 'pipe'] });
如果detached
選項被設置,子進程將成為新進程組的領導。這使得在父進程退出后,子進程繼續(xù)執(zhí)行成為可能。
默認情況下,父進程會等待脫離了的子進程退出。要阻止父進程等待一個給出的子進程,請使用child.unref()
方法,則父進程的事件循環(huán)的計數(shù)中將不包含這個子進程。
一個脫離的長時間運行的進程,以及將它的輸出重定向到文件中的例子:
var fs = require('fs'),
spawn = require('child_process').spawn,
out = fs.openSync('./out.log', 'a'),
err = fs.openSync('./out.log', 'a');
var child = spawn('prg', [], {
detached: true,
stdio: [ 'ignore', out, err ]
});
child.unref();
當使用detached
選項創(chuàng)建一個長時間運行的進程時,進程不會保持運行除非向它提供了一個不連接到父進程的stdio
的配置。如果繼承了父進程的stdio
,那么子進程將會繼續(xù)附著在控制終端。
參閱: child_process.exec()
和 child_process.fork()
command String 將要運行的命令,參數(shù)使用空格隔開
options Object
/bin/sh
, 在Windows中為cmd.exe
, Shell應當能識別 -c
開關在UNIX中,或 /s /c
在Windows中。 在Windows中,命令行解析應當能兼容cmd.exe
)gid Number 設置進程組的ID
callback Function
stderr Buffer
在Shell中運行一個命令,并緩存命令的輸出。
var exec = require('child_process').exec,
child;
child = exec('cat *.js bad_file | wc -l',
function (error, stdout, stderr) {
console.log('stdout: ' + stdout);
console.log('stderr: ' + stderr);
if (error !== null) {
console.log('exec error: ' + error);
}
});
回調函數(shù)的參數(shù)是error
,stdout
,stderr
。在成功時,error
將會是null
。在發(fā)生錯誤時,error
將會是一個Error
實例,error.code
將會是子進程的退出碼,error.signal
將會被設置為結束進程的信號。
第二個可選的參數(shù)用于指定一些配置,默認值為:
{ encoding: 'utf8',
timeout: 0,
maxBuffer: 200*1024,
killSignal: 'SIGTERM',
cwd: null,
env: null }
如果timeout
大于0,那么子進程在運行時超過timeout
時將會被殺死。子進程使用killSignal
信號結束(默認為: 'SIGTERM')。maxBuffer
指定了stdout
,stderr
中的最大數(shù)據(jù)量(字節(jié)),如果超過了這個數(shù)據(jù)量子進程也會被殺死。
注意:不像POSIX中的exec()
,child_process.exec()
不替換已經(jīng)存在的進程并且使用一個SHELL去執(zhí)行命令。
args 字符串參數(shù)數(shù)組
options Object-cwd String 子進程的當前工作目錄-env Object 環(huán)境變量鍵值對
gid Number 設置進程組的ID
callback Function
stderr Buffer
Return: ChildProcess object
這個方法和child_process.exec()
相似,除了它不是使用一個子SHELL執(zhí)行命令而是直接執(zhí)行文件。因此它比child_process.exec
稍許精簡一些。它們有相同的配置。
args Array 字符串參數(shù)數(shù)組
options Object
true
,子進程的stdin
,stdout
和stderr
將會被關聯(lián)至父進程,否則,它們將會從父進程中繼承。(默認為:false
)Return: ChildProcess object
這個方法是spawn()
的特殊形式,用于創(chuàng)建io.js
進程。返回的對象除了擁有ChildProcess
實例的所有方法,還有一個內建的通信信道。詳情參閱child.send(message, [sendHandle])
。
這些io.js
子進程都是全新的V8實例。每個新的io.js
進程都至少需要30ms啟動以及10mb的內存。所以,你不能無休止地創(chuàng)建它們。
options
對象中的execPath
屬性可以用非當前io.js
可執(zhí)行文件來創(chuàng)建子進程。這需要小心使用,并且缺省情況下會使用子進程上的NODE_CHANNEL_FD
環(huán)境變量所指定的文件描述符來通訊。該文件描述符的輸入和輸出假定為以行分割的JSON對象。
注意:不像POSIX中的fork()
,child_process.fork()
不會復制當前進程。
以下這些方法是同步的,意味著它們會阻塞事件循環(huán)。直到被創(chuàng)建的進程退出前,代碼都將停止執(zhí)行。
這些同步方法對簡化大多數(shù)腳本任務都十分有用,并對簡化應用配置的加載/執(zhí)行也之分有用。
args Array 字符串參數(shù)數(shù)組
options Object
stdin
傳入被創(chuàng)建的進程的值,提供這個值將會覆蓋stdio[0]
stdio
配置undefined
)encoding String 被用于所有stdio
輸入和輸出的編碼(默認:'buffer')
return: Object
stdio
輸出結果的數(shù)組stdout
的內容stderr
的內容spawnSync
會在子進程完全結束后才返回。當運行超時或被傳遞killSignal
時,這個方法會等到進程完全退出才返回。也就是說,如果子進程處理了SIGTERM
信號并且沒有退出,你的父進程會繼續(xù)阻塞。
args Array 字符串參數(shù)數(shù)組
options Object
stdin
傳入被創(chuàng)建的進程的值,提供這個值將會覆蓋stdio[0]
stdio
配置(默認: 'pipe'),stderr
默認得將會輸出到父進程的stderr
,除非指定了stdio
undefined
)encoding String 被用于所有stdio
輸入和輸出的編碼(默認:'buffer')
return: Buffer|String 此命令的stdout
execFileSync
會在子進程完全結束后才返回。當運行超時或被傳遞killSignal
時,這個方法會等到進程完全退出才返回。也就是說,如果子進程處理了SIGTERM
信號并且沒有退出,你的父進程會繼續(xù)阻塞。
如果子進程超時或有一個非零的狀態(tài)碼,這個方法會拋出一個錯誤。這個錯誤對象與child_process.spawnSync
的錯誤對象相同。
command 將要運行的命令
options Object
stdin
傳入被創(chuàng)建的進程的值,提供這個值將會覆蓋stdio[0]
stdio
配置(默認: 'pipe'),stderr
默認得將會輸出到父進程的stderr
,除非指定了stdio
undefined
)encoding String 被用于所有stdio
輸入和輸出的編碼(默認:'buffer')
return: Buffer|String 此命令的stdout
execSync
會在子進程完全結束后才返回。當運行超時或被傳遞killSignal
時,這個方法會等到進程完全退出才返回。也就是說,如果子進程處理了SIGTERM
信號并且沒有退出,你的父進程會繼續(xù)阻塞。
如果子進程超時或有一個非零的狀態(tài)碼,這個方法會拋出一個錯誤。這個錯誤對象與child_process.spawnSync
的錯誤對象相同。
更多建議: