node.js: readSync from stdin?
Stdinnode.jsSynchronousStdin Problem Overview
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
Stdin Solutions
Solution 1 - Stdin
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.
Solution 2 - Stdin
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
Solution 3 - Stdin
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 ofnode.js v0.10.4
. - Different platforms behave slightly differently; I've looked at
OS X 10.8.3
andWindows 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.
throw 'ERROR: interactive stdin input not supported.';
} 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));
}
Solution 4 - Stdin
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.
Solution 5 - Stdin
I found a library that should be able to accomplish what you need: https://github.com/anseki/readline-sync
Solution 6 - Stdin
Here is the implementation with async await
. In the below code, the input is taken from standard input and after receiving data the standard input is stopped waiting for data by using process.stdin.pause();
.
// This function reads only one line on console synchronously. After pressing `enter` key the console will stop listening for data.
function readlineSync() {
return new Promise((resolve, reject) => {
process.stdin.resume();
process.stdin.on('data', function (data) {
process.stdin.pause(); // stops after one line reads
resolve(data);
});
});
}
// entry point
async function main() {
let inputLine1 = await readlineSync();
console.log('inputLine1 = ', inputLine1);
let inputLine2 = await readlineSync();
console.log('inputLine2 = ', inputLine2);
console.log('bye');
}
main();
Solution 7 - Stdin
Important: I've just been informed by a Node.js contributor that .fd
is undocumented and serves as a means for internal debugging purposes. Therefore, one's code should not reference this, and should manually open the file descriptor with fs.open/openSync
.
In Node.js 6, it's also worth noting that creating an instance of Buffer
via its constructor with new
is deprecated, due to its unsafe nature. One should use Buffer.alloc
instead:
'use strict';
const fs = require('fs');
// small because I'm only reading a few bytes
const BUFFER_LENGTH = 8;
const stdin = fs.openSync('/dev/stdin', 'rs');
const buffer = Buffer.alloc(BUFFER_LENGTH);
fs.readSync(stdin, buffer, 0, BUFFER_LENGTH);
console.log(buffer.toString());
fs.closeSync(stdin);
Also, one should only open and close the file descriptor when necessary; doing this every time one wishes to read from stdin results in unnecessary overhead.
Solution 8 - Stdin
function read_stdinSync() {
var b = new Buffer(1024)
var data = ''
while (true) {
var n = fs.readSync(process.stdin.fd, b, 0, b.length)
if (!n) break
data += b.toString(null, 0, n)
}
return data
}
Solution 9 - Stdin
The following code reads sync from stdin. Input is read up until a newline / enter key. The function returns a string of the input with line feeds (\n) and carriage returns (\r) discarded. This should be compatible with Windows, Linux, and Mac OSX. Added conditional call to Buffer.alloc (new Buffer(size) is currently deprecated, but some older versions lack Buffer.alloc.
function prompt(){
var fs = require("fs");
var rtnval = "";
var buffer = Buffer.alloc ? Buffer.alloc(1) : new Buffer(1);
for(;;){
fs.readSync(0, buffer, 0, 1); //0 is fd for stdin
if(buffer[0] === 10){ //LF \n return on line feed
break;
}else if(buffer[0] !== 13){ //CR \r skip carriage return
rtnval += new String(buffer);
}
}
return rtnval;
}
Solution 10 - Stdin
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.
Solution 11 - Stdin
I wrote this module to read one line at a time from file or stdin. The module is named as line-reader
which exposes an ES6 *Generator function
to iterate over one line at a time. here is a code sample(in TypeScript) from readme.md.
import { LineReader } from "line-reader"
// FromLine and ToLine are optional arguments
const filePathOrStdin = "path-to-file.txt" || process.stdin
const FromLine: number = 1 // default is 0
const ToLine: number = 5 // default is Infinity
const chunkSizeInBytes = 8 * 1024 // default is 64 * 1024
const list: IterableIterator<string> = LineReader(filePathOrStdin, FromLine, ToLine, chunkSizeInBytes)
// Call list.next to iterate over lines in a file
list.next()
// Iterating using a for..of loop
for (const item of list) {
console.log(item)
}
Apart from above code, you can also take a look at src > tests
folder in the repo.
Note:-
line-reader module doesn't read all stuff into memory instead it uses generator function to generate lines async or sync.