Reputation: 37085
I have a node.js script/server that reads some input from stdin when its launched. However, sometimes there's no data to be passed in. This is troublesome because it seems like in this case neither the data
nor end
events are called. How can I detect when this is the case in the node.js code?
I'd like to avoid having to append special "end" characters at the end of the input, so as not to inconvenience the client. The associated code is below:
var newHTML = '';
var gfm = spawn(__dirname + '/node_modules/docter/bin/github-flavored-markdown.rb');
process.stdin.on('data', function(chunk){
gfm.stdin.write(chunk);
});
process.stdin.on('end', function(){
gfm.stdin.end();
});
gfm.stdout.on('data', function(data) {
newHTML += data;
});
gfm.on('exit',function(ecode){
socket.emit('newContent', newHTML);
});
process.stdin.resume();
Upvotes: 21
Views: 8812
Reputation: 377
With timeouts and using promises you can get something nice going.
export function getStdin(timeout: number = 10): Promise<string> {
return new Promise<string>((resolve, reject) => {
// Store the data from stdin in a buffer
let buffer = ''
process.stdin.on('data', (d) => (buffer += d.toString()))
// Stop listening for data after the timeout, otherwise hangs indefinitely
const t = setTimeout(() => {
process.stdin.destroy()
resolve('')
}, timeout)
// Listen for end and error events
process.stdin.on('end', () => {
clearTimeout(t)
resolve(buffer.trim())
})
process.stdin.on('error', reject)
})
}
Basically you can wait until stdin is ended (which does not happen if left empty) OR set a timeout after which you consider stdin as empty.
Upvotes: 0
Reputation: 7204
The way stdin
works, what you want is not directly possible.
As others have pointed out, the end
event will never trigger without something like < /dev/null
to generate that EOF. Otherwise the program waits for the terminal to send a ^D
.
An alternative approach that might work for you is to set a timeout that expires after a short time if there is no activity on stdin
. This is less than ideal, but works:
function handleData(chunk) {
//clearTimeout(timeout);
gfm.stdin.write(chunk);
}
function handleEnd() {
//clearTimeout(timeout);
gfm.stdin.end();
}
function handleTimeout() {
process.stdin.removeListener('data', handleData);
process.stdin.removeListener('end', handleEnd);
// Do whatever special handling is needed here.
gfm.stdin.end();
}
const timeoutMilliseconds = 100;
process.stdin.on('data', handleData);
process.stdin.on('end', handleEnd);
const timeout = setTimeout(handleTimeout, timeoutMilliseconds);
// You could skip the rest of this code if you just add `clearTimeout(timeout)`
// to the body of `handleData` and `handleEnd` but that would call `clearTimeout` excessively.
function stopTimeout() {
process.stdin.removeListener('data', stopTimeout)
process.stdin.removeListener('end', stopTimeout);
clearTimeout(timeout);
}
process.stdin.on('data', stopTimeout);
process.stdin.on('end', stopTimeout);
Upvotes: 5
Reputation: 1920
Something you can do is to make your app accept an argument like -s
which makes it read from stdin.
That's what LiveScript's CLI tool does:
-s, --stdin read stdin
Upvotes: 5
Reputation: 57974
I believe what may be happening is, you are not giving a stdin steam at all.
Øystein Steimler's example shows you feeding /dev/null into your app:
node pipe.js < /dev/null
However, never addressed when you do not stream stdin to the app at all. Just running node pipe.js
will not exit because it is still waiting for stdin.
You can test this yourself with other unix programs, for example cat
Try running this:
cat < /dev/null
Now try just running:
cat
It does not exit because it is waiting for stdin. You can type into the terminal and send to the program by pressing enter. It will still not exit (and wait for more input) until it receives the EOF which you can do with ctrl+d
Upvotes: 6
Reputation: 1617
An empty or no STDIN stream is detected by the end
event from process.stdin
.
This simple script stdin.js
demonstrates that:
process.stdin.on( 'data', function(data) { console.log( data ) } );
process.stdin.on( 'end', function() { console.log( 'EOF' ) } );
Different scenarios:
$ echo test | node stdin.js
<Buffer 74 65 73 74 0a>
EOF
$ echo -n | node stdin.js
EOF
$ node stdin.js < /dev/null
EOF
$
This script pipe.js
demonstrates that using pipe to a spawned child process works very well:
var spawn = require('child_process').spawn;
var cat = spawn( '/bin/cat' );
cat.stdout.on( 'data', function(data) { console.log( data ) } );
cat.stdout.on( 'end', function() { console.log( 'EOF' ) } );
process.stdin.pipe(cat.stdin);
As expected:
$ echo test | node pipe.js
<Buffer 74 65 73 74 0a>
EOF
$ echo -n | node pipe.js
EOF
$ node pipe.js < /dev/null
EOF
$
Upvotes: 6