node.js: readSync from stdin?

Is it possible to synchronously read from stdin in node.js? Because I'm writing a brainfuck to JavaScript compiler in JavaScript (just for fun). Brainfuck supports a read operation which needs to be implemented synchronously.

I tried this:

const fs = require('fs');
var c = fs.readSync(0,1,null,'utf-8');
console.log('character: '+c+' ('+c.charCodeAt(0)+')');

But this only produces this output:

fs:189
  var r = binding.read(fd, buffer, offset, length, position);
              ^
Error: EAGAIN, Resource temporarily unavailable
    at Object.readSync (fs:189:19)
    at Object.<anonymous> (/home/.../stdin.js:3:12)
    at Module._compile (module:426:23)
    at Module._loadScriptSync (module:436:8)
    at Module.loadSync (module:306:10)
    at Object.runMain (module:490:22)
    at node.js:254:10

Have you tried:

fs=require('fs');
console.log(fs.readFileSync('/dev/stdin').toString());

However, it will wait for the ENTIRE file to be read in, and won't return on \n like scanf or cin.

After fiddling with this for a bit, I found the answer:

process.stdin.resume();
var fs = require('fs');
var response = fs.readSync(process.stdin.fd, 100, 0, "utf8");
process.stdin.pause();

response will be an array with two indexes, the first being the data typed into the console and the second will be the length of the data including the newline character.

It was pretty easy to determine when you console.log(process.stdin) which enumerates all of the properties including one labeled fd which is of course the name of the first parameter for fs.readSync()

Enjoy! :D

An updated version of Marcus Pope's answer that works as of node.js v0.10.4:

Please note:

  • In general, node's stream interfaces are still in flux (pun half-intended) and are still classified as 2 - Unstable as of node.js v0.10.4.
  • Different platforms behave slightly differently; I've looked at OS X 10.8.3 and Windows 7: the major difference is: synchronously reading interactive stdin input (by typing into the terminal line by line) only works on Windows 7.

Here's the updated code, reading synchronously from stdin in 256-byte chunks until no more input is available:

var fs = require('fs');
var BUFSIZE=256;
var buf = new Buffer(BUFSIZE);
var bytesRead;

while (true) { // Loop as long as stdin input is available.
    bytesRead = 0;
    try {
        bytesRead = fs.readSync(process.stdin.fd, buf, 0, BUFSIZE);
    } catch (e) {
        if (e.code === 'EAGAIN') { // 'resource temporarily unavailable'
            // Happens on OS X 10.8.3 (not Windows 7!), if there's no
            // stdin input - typically when invoking a script without any
            // input (for interactive stdin input).
            // If you were to just continue, you'd create a tight loop.
            console.error('ERROR: interactive stdin input not supported.');
            process.exit(1);
        } else if (e.code === 'EOF') {
            // Happens on Windows 7, but not OS X 10.8.3:
            // simply signals the end of *piped* stdin input.
            break;          
        }
        throw e; // unexpected exception
    }
    if (bytesRead === 0) {
        // No more stdin input available.
        // OS X 10.8.3: regardless of input method, this is how the end 
        //   of input is signaled.
        // Windows 7: this is how the end of input is signaled for
        //   *interactive* stdin input.
        break;
    }
  // Process the chunk read.
  console.log('Bytes read: %s; content:\n%s', bytesRead, buf.toString(null, 0, bytesRead));
}

I've no idea when this showed up but this is a helpful step forward: http://nodejs.org/api/readline.html

var readline = require('readline');

var rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
  terminal: false
});

rl.on('line', function (cmd) {
  console.log('You just typed: '+cmd);
});

Now I can read line-at-a-time from stdin. Happy days.

I wrote a little C++ add-on module that make synchronous reading on keyboard (https://npmjs.org/package/kbd).

I used this workaround on node 0.10.24/linux:

var fs = require( "fs" )
var fd = fs.openSync( "/dev/stdin", "rs" )
fs.readSync( fd, new Buffer( 1 ), 0, 1 )
fs.closeSync( fd )

This code waits for pressing ENTER. It reads one character from line, if user enters it before pressing ENTER. Other characters will be remained in the console buffer and will be read on subsequent calls to readSync.

I found a library that should be able to accomplish what you need: https://github.com/anseki/readline-sync