In Node, the child_process module is very important. Mastering it opens a new door in the World of Node.
Here’s a simple example:
const spawn = require('child_process').spawn;
const ls = spawn('ls'['-lh'.'/usr']);
ls.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
ls.stderr.on('data', (data) => {
console.log(`stderr: ${data}`);
});
ls.on('close', (code) => {
console.log(`child process exited with code ${code}`);
});Copy the code
Several ways to create child processes
Matters needing attention:
- The following are all ways to create a child process asynchronously, each of which has a synchronous version.
.exec()
,.execFile()
,.fork()
The bottom is all through.spawn()
The implementation..exec()
,execFile()
Additional callbacks are provided to execute when the child process stops.
child_process.spawn(command[, args][, options]) child_process.exec(command[, options][, callback]) child_process.execFile(file[, args][, options][, callback]) child_process.fork(modulePath[, args][, options])
child_process.exec(command[, options][, callback])
Create a shell and execute commands inside the shell. After execution, pass stdout and stderr as arguments to the callback method.
spawns a shell and runs a command within that shell, passing the stdout and stderr to a callback function when complete.
Examples are as follows:
- Successful execution,
error
fornull
; Execution failed,error
forError
Instance.error.code
Is the error code, stdout
,stderr
Is standard output, standard error. The default is a string, unlessoptions.encoding
forbuffer
var exec = require('child_process').exec; // Success storiesexec('ls -al'.function(error, stdout, stderr){
if(error) {
console.error('error: ' + error);
return;
}
console.log('stdout: ' + stdout);
console.log('stderr: '+ typeof stderr); }); // Failed examplesexec('ls hello.txt'.function(error, stdout, stderr){
if(error) {
console.error('error: ' + error);
return;
}
console.log('stdout: ' + stdout);
console.log('stderr: ' + stderr);
});Copy the code
Parameter Description:
cwd
: Indicates the current working path.env
: Environment variables.encoding
: Encoding, defaultutf8
.shell
: the shell used to execute commands, the default on Unix/bin/sh
On Windows, the default iscmd.exe
.timeout
: The default value is 0.killSignal
Default is:SIGTERM
.uid
: UID of the executing process.gid
: GID of the execution process.maxBuffer
: Maximum amount of data (in bytes) allowed for standard output, error output, if exceeded, the child process will be killed. Default: 200*1024
Remark:
- if
timeout
Greater than zero, then, when the child process runs greater thantimeout
Milliseconds, then, are sent to the processkillSignal
Specified signals (e.gSIGTERM
). - If it runs without error, then
error
fornull
. If something goes wrong, then,error.code
Exist Code,error.signal
Is set to terminate the process. (e.g.,CTRL+C
When sendingSIGINT
)
Risk items
Incoming commands, if entered by the user, may pose risks similar to SQL injection, such as
exec('ls hello.txt; rm -rf *'.function(error, stdout, stderr){
if(error) {
console.error('error: ' + error);
// return;
}
console.log('stdout: ' + stdout);
console.log('stderr: ' + stderr);
});
Copy the code
Note the matters
Note: Unlike the exec(3) POSIX system call, child_process.exec() does not replace the existing process and uses a shell to execute the command.
child_process.execFile(file[, args][, options][, callback])
Similar to.exec(), except that a new shell is not created. There are at least two implications
- than
child_process.exec()
It’s more efficient. (Actual to be tested) - Some operations, such as I/O redirection, file glob, etc. are not supported.
similar to child_process.exec() except that it spawns the command directly without first spawning a shell.
File: specifies the name or path of the executable file.
Example:
var child_process = require('child_process');
child_process.execFile('node'['--version'].function(error, stdout, stderr){
if(error){
throw error;
}
console.log(stdout);
});
child_process.execFile('/ Users/a /. NVM/versions/node/v6.1.0 / bin/node'['--version'].function(error, stdout, stderr){
if(error){
throw error;
}
console.log(stdout);
});Copy the code
====== Expand reading =======
From the node source code, the biggest difference between exec() and execFile() is whether or not a shell is created. (inside execFile(), options.shell === false), then the shell can be set manually. The following code is almost equivalent. The shell Settings in WIN are different. If you are interested, you can try them out for yourself.
Note: execFile() is ultimately implemented internally via spawn(), if {shell: ‘/bin/bash’} is not set, the command is parsed differently inside spawm(), execFile(‘ ls-al.’) will report an error.
var child_process = require('child_process');
var execFile = child_process.execFile;
var exec = child_process.exec;
exec('ls -al .'.function(error, stdout, stderr){
if(error){
throw error;
}
console.log(stdout);
});
execFile('ls -al .', {shell: '/bin/bash'}, function(error, stdout, stderr){
if(error){
throw error;
}
console.log(stdout);
});Copy the code
child_process.fork(modulePath[, args][, options])
ModulePath: module in which the child process runs.
Parameter description :(duplicate parameter description is not listed here)
execPath
: The executable used to create the child process/usr/local/bin/node
. In other words, you can passexecPath
To specify the specific node executable path. (such as multiple Node versions)execArgv
: a list of string arguments passed to the executable. The default isprocess.execArgv
To be consistent with the parent process.silent
Default is:false
That is, of the child processstdio
Inherits from the parent process. If it istrue
The directpipe
Of the child processchild.stdin
,child.stdout
And so on.stdio
: If statedstdio
, will overwritesilent
Option Settings.
Example 1: Silent
parent.js
var child_process = require('child_process'); // Output from the child is printedfalse// Inherit from parent child_process.fork('./child.js', {
silent: false}); // Example 2: Output from the silent child is not printedtrue// pipe to child_process.fork('./silentChild.js', {
silent: true}); Output from another silent child var child = child_process.fork('./anotherSilentChild.js', {
silent: true
});
child.stdout.setEncoding('utf8');
child.stdout.on('data'.function(data){
console.log(data);
});Copy the code
child.js
console.log('output from the child');Copy the code
silentChild.js
console.log('output from the silent child');Copy the code
anotherSilentChild.js
console.log('output from another silent child');Copy the code
Example 2: IPC
parent.js
var child_process = require('child_process');
var child = child_process.fork('./child.js');
child.on('message'.function(m){
console.log('message from child: ' + JSON.stringify(m));
});
child.send({from: 'parent'});Copy the code
child.js
process.on('message'.function(m){
console.log('message from parent: ' + JSON.stringify(m));
});
process.send({from: 'child'});Copy the code
The results
➜ IPC git:(Master) Qualify node parent.js message from child: {"from":"child"}
message from parent: {"from":"parent"}Copy the code
Example 3: execArgv
First, see here for the definition of process.execArgv. The general purpose of setting up execArgv is to keep the child in the same execution environment as the parent.
For example, if the parent process specifies harmony, if the child process does not specify harmony, it will kneel.
parent.js
var child_process = require('child_process');
console.log('parent execArgv: ' + process.execArgv);
child_process.fork('./child.js', {
execArgv: process.execArgv
});Copy the code
child.js
console.log('child execArgv: ' + process.execArgv);Copy the code
The results
➜ execArgv git:(master) qualify node --harmony parent-js parentexecArgv: --harmony
child execArgv: --harmonyCopy the code
Example 3: execPath (TODO to be cited)
child_process.spawn(command[, args][, options])
Command: Indicates the command to be executed
Options Parameter description:
argv0
: [String] This item is weird, it works differently on UNinx and Windows. We need to dig a little deeper.stdio
: (Array) | [String] the child stdio. referenceheredetached
: [Boolean] Lets the child process run independently of its parent. The existing differences are also listed on different platforms for specific referencehereshell
| : [Boolean] [String] if it istrue
Run the program in the shell. The default isfalse
. (Useful, for example, to implement.exec() with /bin/sh -c XXX)
Example 1: Basic example
var spawn = require('child_process').spawn;
var ls = spawn('ls'['-al']);
ls.stdout.on('data'.function(data){
console.log('data from child: ' + data);
});
ls.stderr.on('data'.function(data){
console.log('error from child: ' + data);
});
ls.on('close'.function(code){
console.log('child exists with code: ' + code);
});Copy the code
Example 2: Declare STDIO
var spawn = require('child_process').spawn;
var ls = spawn('ls'['-al'], {
stdio: 'inherit'
});
ls.on('close'.function(code){
console.log('child exists with code: ' + code);
});Copy the code
Example 3: Declare using a shell
var spawn = require('child_process').spawn; / / runecho "hello nodejs" | wc
var ls = spawn('bash'['-c'.'echo "hello nodejs" | wc'], {
stdio: 'inherit',
shell: true
});
ls.on('close'.function(code){
console.log('child exists with code: ' + code);
});Copy the code
Example 4: Error handling, which contains two scenarios that have different handling styles.
- Scenario 1: The command does not exist, and an error occurs when creating a child process.
- Scenario 2: The command exists, but an error occurs during execution.
var spawn = require('child_process').spawn;
var child = spawn('bad_command');
child.on('error', (err) => {
console.log('Failed to start child process 1.');
});
var child2 = spawn('ls'['nonexistFile']);
child2.stderr.on('data'.function(data){
console.log('Error msg from process 2: ' + data);
});
child2.on('error', (err) => {
console.log('Failed to start child process 2.');
});Copy the code
The result is as follows.
➜ spawn git:(master) Qualify node error/error.js Failed to start child process 1. Error MSG from Process 2: ls: nonexistFile: No such file or directoryCopy the code
Example 5: echo “hello nodejs” | grep “nodejs”
// echo "hello nodejs" | grep "nodejs"
var child_process = require('child_process');
var echo = child_process.spawn('echo'['hello nodejs']);
var grep = child_process.spawn('grep'['nodejs']);
grep.stdout.setEncoding('utf8');
echo.stdout.on('data'.function(data){
grep.stdin.write(data);
});
echo.on('close'.function(code){
if(code! ==0){ console.log('echo exists with code: ' + code);
}
grep.stdin.end();
});
grep.stdout.on('data'.function(data){
console.log('grep: ' + data);
});
grep.on('close'.function(code){
if(code! ==0){ console.log('grep exists with code: '+ code); }});Copy the code
Running results:
➜ spawn Git :(Master) Qualify node pipe/pipe.js grep: hello nodejsCopy the code
About the options. The stdio
Default: [‘pipe’, ‘pipe’, ‘pipe’], which means:
- Child. stdin, child.stdout are not
undefined
- You can listen in
data
Event to retrieve data.
Based on example
var spawn = require('child_process').spawn;
var ls = spawn('ls'['-al']);
ls.stdout.on('data'.function(data){
console.log('data from child: ' + data);
});
ls.on('close'.function(code){
console.log('child exists with code: ' + code);
});Copy the code
Write to child.stdin.write()
var spawn = require('child_process').spawn;
var grep = spawn('grep'['nodejs']);
setTimeout(function(){
grep.stdin.write('hello nodejs \n hello javascript');
grep.stdin.end();
}, 2000);
grep.stdout.on('data'.function(data){
console.log('data from grep: ' + data);
});
grep.on('close'.function(code){
console.log('grep exists with code: ' + code);
});Copy the code
Asynchronous vs Synchronous
Most of the time, child processes are created asynchronously. That is, it does not block the current event loop, which is helpful for performance.
Of course, there are times when a synchronous approach is more convenient (blocking the event loop), such as when executing shell scripts through child processes.
Node also provides synchronized versions, such as:
- spawnSync()
- execSync()
- execFileSync()
About the options. Detached
Since I didn’t test it on Windows, I posted the original text first
On Windows, setting options.detached to true makes it possible for the child process to continue running after the parent exits. The child will have its own console window. Once enabled for a child process, it cannot be disabled.
A non-window is a platform representation
On non-Windows platforms, if options.detached is set to true, the child process will be made the leader of a new process group and session. Note that child processes may continue running after the parent exits regardless of whether they are detached or not. See setsid(2) for more information.
Default: The parent waits for the child to finish.
The child process. As you can see, there’s a timer running
var times = 0;
setInterval(function(){
console.log(++times);
}, 1000);Copy the code
Run the following code to see that the parent process is holding on.
var child_process = require('child_process');
child_process.spawn('node'['child.js'], {
// stdio: 'inherit'
});
Copy the code
Exit the parent process with child.unref()
Call child.unref() to remove the child from the parent’s event loop. The parent process can then exit happily. There are a few key points here
- call
child.unref()
- Set up the
detached
fortrue
- Set up the
stdio
forignore
(Easy to forget)
var child_process = require('child_process');
var child = child_process.spawn('node'['child.js'], {
detached: true,
stdio: 'ignore'// Note: If it is not ignore, the parent process will not exit // stdio:'inherit'
});
child.unref();Copy the code
Redirect stdio to a file
Instead of setting stdio to ignore directly, you can redirect it to a local file.
var child_process = require('child_process');
var fs = require('fs');
var out = fs.openSync('./out.log'.'a');
var err = fs.openSync('./err.log'.'a');
var child = child_process.spawn('node'['child.js'], {
detached: true,
stdio: ['ignore', out, err]
});
child.unref();Copy the code
Difference between exec() and execFile()
First, exec() internally calls execFile() for implementation, and execFile() internally calls spawn() for implementation.
exec() -> execFile() -> spawn()
Second, execFile() internally defaults to false for options.shell and exec() does not.
Class: ChildProcess
- through
child_process.spawn()
And so on, usually not directly with the constructor to create. - inherited
EventEmitters
, so there are.on()
Methods.
Various events
close
Triggered when the STdio stream is off. This event is different from exit because multiple processes can share the same STDIO stream. Parameters: code (exit code if the child process exits by itself), signal (signal to terminate the child process) Question: Must code exist? For example, if you kill a child process, what is code?
exit
Parameters: code, signal, code is the exit code if the child process exits by itself, otherwise null; Signal is the signal to terminate the process if the child process is terminated by a signal, otherwise null. One of the two is definitely not null. Note: The child’s STdio Stream may still be open when the exit event is triggered. (Scene?) In addition, NodeJS listens for SIGINT and SIGTERM signals, meaning that nodeJS does not immediately exit when it receives them, but does some cleanup and then rethrows them. At this point, js can clean up, such as closing the database, etc. TODO question: js can also not exit ????)
SIGINT: interrupt, a program termination signal, usually emitted when the user presses CTRL+C, to inform the foreground process to terminate the process. SIGTERM: terminate, a program termination signal that can be blocked and processed, usually to require the program itself to exit normally. The shell command kill generates this signal by default. If the signal cannot be terminated, we try SIGKILL (forced termination).
Also, note that Node.js establishes signal handlers for SIGINT and SIGTERM and Node.js processes will not terminate immediately due to receipt of those signals. Rather, Node.js will perform a sequence of cleanup actions and then will re-raise the handled signal.
error
An error is raised when the following occurs. Exit may or may not fire when error does. (Inside is broken)
- Unable to create child process.
- The process cannot be killed. (TODO example)
- Failed to send message to child process. (TODO example)
message
Triggered when process.send() is used to send a message. Parameters: Message, which is a JSON object, or Primitive Value; SendHandle, net.socket object, or net.server object (when is TODO??)
.connected: Set to false when.disconnected() is called. Represents whether messages can be received from or sent to child processes.
.disconnect() : closes the IPC channel between parent and child processes. When this method is called, the Disconnect event fires. If the child process is a Node instance (created by child_process.fork()), then an active call to process.disconnect() can also be made inside the child process to terminate the IPC channel. Refer to the process. Disconnect. Question: If a child is forked and the child starts the HTTP server, what is the effect of the parent’s.disconnect() call? (TODO verification??)
Non-important cheat points
CMD and BAT on Windows
The importance of the distinction between child_process.exec() and child_process.execFile() can vary based on platform. On Unix-type operating systems (Unix, Linux, OSX) child_process.execFile() can be more efficient because it does not spawn a shell. On Windows, however, .bat and .cmd files are not executable on their own without a terminal, and therefore cannot be launched using child_process.execFile(). When running on Windows, .bat and .cmd files can be invoked using child_process.spawn() with the shell option set, with child_process.exec(), or by spawning cmd.exe and passing the .bat or .cmd file as an argument (which is what the shell option and child_process.exec() do).
// On Windows Only ...
const spawn = require('child_process').spawn;
const bat = spawn('cmd.exe'['/c'.'my.bat']);
bat.stdout.on('data', (data) => {
console.log(data);
});
bat.stderr.on('data', (data) => {
console.log(data);
});
bat.on('exit', (code) => {
console.log(`Child exited with code ${code}`);
});
// OR...
const exec = require('child_process').exec;
exec('my.bat', (err, stdout, stderr) => {
if (err) {
console.error(err);
return;
}
console.log(stdout);
});Copy the code
Process the title
Note: Certain platforms (OS X, Linux) will use the value of argv[0] for the process title while others (Windows, SunOS) will use command.
Note: Node.js currently overwrites argv[0] with process.execPath on startup, so process.argv[0] in a Node.js child process will not match the argv0 parameter passed to spawn from the parent, retrieve it with the process.argv0 property instead.
A problem with the running order of the code
p.js
const cp = require('child_process');
const n = cp.fork(`${__dirname}/sub.js`);
console.log('1');
n.on('message', (m) => {
console.log('PARENT got message:', m);
});
console.log('2');
n.send({ hello: 'world' });
console.log('3');Copy the code
sub.js
console.log('4');
process.on('message', (m) => {
console.log('CHILD got message:', m);
});
process.send({ foo: 'bar' });
console.log('5');Copy the code
Run node p.js and print the following
➜ ch node p.js 1 2 3 4 5 PARENT got message: {foo:'bar' }
CHILD got message: { hello: 'world' }Copy the code
Let’s do another example
// p2.js
var fork = require('child_process').fork;
console.log('p: 1');
fork('./c2.js');
console.log('p: 2'); // From the test results, sometimes the timer callback is executed before the child process, and sometimes it is executed slower than the child process. const t = 70;setTimeout(function(){
console.log('p: 3 in %s', t);
}, t);
// c2.js
console.log('c: 1');Copy the code
About NODE_CHANNEL_FD
When child_process.fork(), if execPath is specified, parent and child processes communicate via NODE_CHANNEL_FD.
Node.js processes launched with a custom execPath will communicate with the parent process using the file descriptor (fd) identified using the environment variable NODE_CHANNEL_FD on the child process. The input and output on this fd is expected to be line delimited JSON objects.
A link to the
Official documentation: nodejs.org/api/child_p…