Skip to content

Instantly share code, notes, and snippets.

Forked from isaacs/stdio-2.js
Created Jan 22, 2016
What would you like to do?
In a child process, each of the stdio streams may be set to
one of the following:
1. A new file descriptor in the child, dup2'ed to the parent and
exposed to JS as a Stream object.
2. A copy of a file descriptor from the parent, with no other
added magical stuff.
3. A black hole - no pipe created.
The trouble with using raw file descriptor integers for this is
that they are not portable to Windows.
The 'stdio' option to child_process.spawn() is an array where each
index corresponds to a fd in the child. The value is one of the following:
1. `null` or `undefined` - Use the default value. For 0,1,2,
this is the same as 'pipe'. For any higher value, 'ignore'
2. 'ignore' - Open the fd in the child, and /dev/null it
3. 'pipe' - Create a new dup2 pipe, and expose a stream object to JS.
4. 'ipc' - The same as 'pipe', but set up to send/receive IPC messages and
pass server/socket/FD handles for use with cluster.
Note: A ChildProcess may have at most *one* IPC stdio file descriptor.
Setting this option enables the ChildProcess.send() method. If the
child writes JSON messages to this file descriptor, then this will trigger
ChildProcess.on('message'). If the child is a Node.js program, then
the presence of an IPC channel will enable process.send() and
5. positive integer - call tty_wrap.guessHandleType. Create the appropriate
pipe_wrap type of thing for this. Note that this will generally not
be a portable way to share a socket with the child process as a
stdio fd.
6. Any HandleWrap object - If the object is a HandleWrap object,
then the underlying TCP, Pipe, or File handle is shared with the
child process.
7. Any {handle:<instanceof HandleWrap>} object - If the object has a
`handle` member which is a HandleWrap object of some sort, then
treat this as #5.
8. Any {fd:<positive integer>} object - If the object has an ingeter `fd` member,
then treat this as #4.
9. Any negative integer: The same as 'pipe' (Backwards compatibility with
10. Anything else: throw.
As a shorthand, the `stdio` argument may also be one of the following
strings, rather than an array:
* `ignore` --> ['ignore', 'ignore', 'ignore']
* `pipe` --> ['pipe', 'pipe', 'pipe'] == [-1,-1,-1]
* `inherit` --> [process.stdin, process.stdout, process.stderr] == [0,1,2]
* `spawn`, `exec`, `execFile` --> ['pipe', 'pipe', 'pipe']
* `fork` --> ['ipc', 'pipe', 'pipe'] or ['ipc', 'ignore', 'ignore'] if
{ silent: true }
Since cluster just uses child_process.fork, it'll use the same defaults.
This will require a bit of refactoring in other parts of node besides
* TCP and TTY sockets have a "handle" member added and exposed explicitly.
* createPipe() should be blessed as a public API so that users can create
arbitrary duplex streams for stdio.
## createPipe([options])
* `options` {Object}
* `ipc` {Boolean} Default=false
* `readable` {Boolean} Default=true
* `writable` {Boolean} Default=true
* return: Pipe stream object
By default, all created pipes are duplex. The readable/writable aspect is
only set at the JavaScript layer to help prevent doing the wrong sort of
stream.pipe(). At the low-level, there is no special half-duplex behavior.
IPC is only enabled if explicitly requested, usually by setting 'ipc' as one
of the stdio FD options.
The return object is a Stream object. It has a "remote" member which
is an opaque object representing the other end of the pipe pair, and
is actually used by spawn.
var p = createPipe()
var c = spawn(cmd, args, { stdio: [ p ] })
p === c.stdin
p.on('data', function (chunk) {
console.error('c.stdin emitted data', chunk)
## Exposure on the ChildProcess object.
The stdin/stdout/stderr members will be set to a Stream object if set to
`pipe`, or null otherwise.
If an `ipc` channel was requested, then the ChildProcess object will have
a `send(msg)` method, and will perhaps `emit('message', obj)` if the child
writes JSON to that channel.
Other stdio fds will be exposed on the `stdio` member. ChildProcess.stdio
is an Array corresponding to the file descriptors opened in the child.
child.stdio[0] === child.stdin, child.stdio[2] === child.stderr, etc.
// Examples
// 1. spawn a child, taking over the parent's terminal.
spawn(cmd, args, { stdio: 'inherit' })
// 2. spawn a child, sharing only stderr
spawn(cmd, args, { stdio: ['pipe', 'pipe', process.stderr] })
// 3. since integer FDs will be inspected and set up appropriately,
// customFds-style will continue to work without modification
spawn(cmd, args, { stdio: [-1, -1, 2] })
// 4. Open an extra fd=4, to interact with programs present a
// startd-style interface.
spawn(cmd, args, { stdio: ['pipe', 'ignore', 'ignore', null, 'pipe'] })
// 5. Provide a socket as the stdin and stdio to a child,
// to interact with programs that present a cgi-style interface
net.createServer(function (conn) {
spawn(cmd, args, { stdio: [ conn, conn, process.stderr ] })
// 6. Same as #5, but log stderr to a file:'error.log', function (er, fd) {
if (er) throw er
net.createServer(function (conn) {
spawn(cmd, args, { stdio: [ conn, conn, fd ] })
// 7. like child_process.fork()
spawn(cmd, args, { stdio: [ 'ipc', 1, 2 ] })
// 7a. like child_process.fork(..., {silent: true})
spawn(cmd, args, { stdio: [ 'ipc', 'ignore', 'ignore' ] })
// 8. interact with some silly unix utility that uses a bunch of weird
// stdio stuff.
// * stdin is a pipe
// * stdout is a JS stream object
// * stderr is mapped to the parent's stdout
// * fd=3 is a JS stream object
// * fd=4 is ignored
// * fd=5 is the parent's stdin (eg, for the user to type in a pass phrase)
// * fd=6 writes to an error log
// * fd=7 can receive file descriptors and handles.
var rw = createPipe()
var ipc = createPipe({ipc: true})"log.txt", function (er, fd) {
if (er) throw er
spawn(cmd, args, { stdio: [ rw, 'pipe', process.stdout, 'pipe', null, 0, fd, ipc ] })
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment